c语言算法实现-笔记-递归中的树

本文深入探讨了二叉树的基本概念、性质与遍历方法,并提供了多种遍历算法的实现细节,包括递归与非递归方式。此外还介绍了如何利用二叉树解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/****
//后的注解是自己的理解.
*****/
树是一种数学上的抽象,在算法的设计与分析中起到了核心的作用,
我们使用树来描述算法的动态性质;
我们创建并用显式的数据结构,这些数据结构都是树的具体实现.
一棵树:满足某种要求的节点与边的一个非空集合.//边是虚拟的,节点才是实的。
对于树来说:任意两点只有唯一一条路径.

自由树>有根树>有序树>M叉树.


二叉树表示: 

	typedef struct node * link;
	struct node{
	    Item item;
	    link left , right;
	};

二叉树与有序森林之间,存在一一对应关系。

图:
定义:一个图由节点集合和连接不同节点对的边的集合组成(任何一对节点至多有一条边相连)。
概念: 简单路径 - 从A节点到B节点的边的序列(节点不重复出现);
连通 - 存在一条简单路径连接任何节点;
环路 - 开头与末尾节点相同的一条简单路径。
图是树的条件(任一条都可以):
* G有N-1条边,并且没有环路;
* G有N-1条边,并且是连通的;
* 只有一条简单路径连接G中的每一对节点;
* G是连通的,但在任一条边被删除后不再保持连通。
**********************************************************************************************************
 二叉树的性质:
 && 一棵有N个内部节点的二叉树有N+1个外部节点.
 && 一棵有N个内部节点的二叉树有2N个链接:N-1个连接到内部节点,N+1个链接到外部节点。
 && 任何带有N个内部节点的二叉树的外部路径长度比内部路径长度大2N。//每一个节点都致使外部路径长度比内部路径长度大2.  
 && 带有N个内部节点的二叉树的高度至少是lg N,至多是N-1.
 && 带有N个内部节点的二叉树的内部路径长度至少是N lg(N/4),至多是N(N-1)/2.
 *********************************************************************************************************
 树的遍历:
 *前序:节点、节点左子树、节点右子树;
 *中序:节点左子树、节点、节点右子树;
 *后序:节点左子树、节点右子树、节点;
 
 //递归遍历
 /**
 visit函数的放置顺序不同,得到不同的遍历。
 **/
void traverse( link h , void( * visit ) (link) ){
	  if (h == null)
		return ;
	 ( *visit )(h);
	  traverse( h->left , visit );
	  traverse( h->right , visit );
}

 //使用下推栈遍历
 从一个抽象栈开始考察,这个栈能够保存数据项或树,以将被遍历的树初始化。然后我们进入一个循环,在这个循环中我们弹出并处理在栈顶的元素,如此继续,直到栈空为止。如果弹出的是一个数据项,visit(),若弹出的是一棵树,就按照希望的顺序执行一系列的入栈操作:
 *对于前序,压入右子树,压入左子树,再压入节点;
 *对于中序,压入右子树,压入节点,再压入左子树;
 *对于后序,压入节点,压入右子树,再压入左子树;
 
 前序遍历的非递归实现(一):

 //按照栈的特点与树的遍历关系,简洁易懂!

void traverse( link h , void ( *visit ) (link) ){
  	STACKinit(max);
  	STACKpush(h);
  	while( !STACKempty() ){
  		(*visit)(h = STACKpop() ) ;
  		if( h->right != NULL )
  			STACKpush( h->right );
  		if( h->left != NULL )
  			STACKpush( h->left );
  	}
}

 前序遍历的非递归实现(二):
 //按照遍历的路线行进,程序冗长。但转中序遍历则极为方便,只需把访问函数放到STACKpop()前即可。
void traverse( link h ){
         STACKinit(max);
	  while( h || !STACKempty() ){
	  	while(h){
	  		(*visit)( h );
	  		STACKpush( h );
	  		h=h->left;
	  	}
	  	if( !STACKempty() ){
	  		h = STACKpop();
	  		h = h->right;
	  	}
	  }
}


 
 中序遍历的非递归实现:
 上述已写,此处略。
 
 后序遍历的非递归实现:
 //需要设置访问标记(想了很久,感觉不标记不行,有想到的,请不吝告知)。
 
 程式一,不好的地方:内部结点要入栈出栈再入栈。
void traverse( link h ){
	  STACKinit(max);
	  do{
	  	while( h ){
	  		STACKpush( h );
	  		h.flag = 0; //右节点未访问,可设置为默认值。
	  		h = h->left;
	  	}
	  	if( !STACKempty() ){
	  		h = STACKpop();
	  		if( h->right == null || h.flag == 1 ){
	  			(*visit)( h );
	  			//h.flag = 1; //标记右节点已访问,无所谓标记不标记
	  			h = null; //防止再入栈,很重要!否则栈将无限增大!
	  		}else{
	  			h.flag = 1;
	  			STACKpush( h ); //右节点未访问,再入栈。
	  			h=h->right;
	  		}
	  	}
	 }while( !STACKempty() );
}

 
 程式二:
 //设置一个标记数组
 
void traverse( link h ){
	  STACKinit(max);
	  do{
	  	while( h ){
	  		STACKpush( h );
	  			
	  		flag[ STACKtop() ]=0; //每次栈顶置0
	  		//h.flag = 0; //右节点未访问,可设置为默认值。
	  			
	  		h = h->left;
	  	}
	  	if( !STACKempty() ){
	  		if( flag[ STACKtop() ] == 1 ){
	  			h = STACKpop();
	  			(*visit)( h );
	  			h = null ;
	  		}else{
	  			h = getSTACKtop();
	  			flag[ STACKtop() ] = 1;
	  			h = h->right;
	  		}
	  		
	  		/***
	  		h = STACKpop();
	  		if( h->right == null || h.flag == 1 ){
	  			(*visit)( h );
				//h.flag = 1; //标记右节点已访问,无所谓标记不标记
	  			h = null; //防止再入栈,很重要!否则栈将无限增大!
	  	        }else{
	  		      h.flag = 1;
	  		      STACKpush( h ); //右节点未访问,再入栈。
	 		      h=h->right;
	  	        }
	  			**/
	          }
	  }while( !STACKempty() );
}


 
 层序遍历二叉树:
 //队列的 先进先出 特性,类似于层递归思想。
void traverse( link h , void (*visit)(link) ){
	  QUEUEinit(max);
	  QUEUEput(h);
	  while( !QUEUEempty() ){
	  	( *visit )( h = QUEUEget() );
	  	if( h->left != NULL )
	  		QUEUEput( h->left );
	  	if( h->right != NULL )
	  		QUEUEput( h->right );
	  }
}


 
******************************************************************************************************
能高效计算二叉树的内部路径长度的程序就是较大的挑战。
*******************************************************************************************************
递归计算树的节点的个数:
int count ( link h ){
	 if( h==null )
	 	return 0;
	 return count( h->left )+count( h->right )+1;
}
 
递归计算树的高度:
int height( link h ){
 	int u , v ;
 	if( h == null )
 		return -1;
 	u = height( h->left );
 	v = height( h->right );
 	return u>v?u:v;
}


********************************************************************************************************
快速打印树:
void printnode( char c , int h ){
	 int i ;
	 for( i=0; i<h ;++i )
	 	printf(" ");
	 printf("%c\n",c);
}
void show( link x ,int h ){
 	if( x==null ){
 		printnode('*',h);
 		return;
	 }
	 show(x->left ,h+1);
	 printnode(x->item,h);
	 show(x->right,h+1);
}


***************************************************************************************************
联赛的构造:要为N>1的数据项构造一个联赛,使用分治策略,把数据项分成两半,为每一半构造一个联赛,并创建一个新的节点,该节点具有到两个联赛的链接,并且有一个数据项,该数据项是两个联赛的根节点的较大数据项的副本。
typedef struct node * link;
struct node {
Item item;
link left,right;
};
link New( Item item, link left ,link right) {
link x = malloc(sizeofof *x);
x->item = item;
x->left = left;
x->right = right;
return x;
}
link max(Item a[] ,int left ,int right) {
int m=(left+right)/2;
Item u,v;
link x =New( a[m] ,null,null);
if( left=right )
return x;
x->left = max( a, left, m);
x->right = max(a ,m+1 ,right);
u = x->left->item;
v = x->right->item;
x->item =u>v:u?v;
return x;
}
***********************************************************************************************
使用前缀表达式构造一棵树:
char *a;
int i;
typedef struct Tnode * link;
struct Tnode{
char token;
link left,right;
};
link New( char token ,link left ,link right) {
link x = malloc(sizeof *x );
x->token = token;
x-left = left;
x->right = right;
return x;
}
link parse() {
char t = a[i++];
link x = New( t, null ,null );
if( t>='+' && t<='/'){
x->left = parse();
x->right = parse();
}
return x;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值