树和二叉树总结

《数据结构(C语言版)》(严蔚敏 吴伟民)这本书也断断续续的看过了好几遍,但都是在书上做做笔记,这次想着就把总结放到网上,一是自己可以经常翻出来读读,二是可以和网友们交流。

因为是从非线性数据结构部分开始重读的,所以本书前面的线性数据结构内容以后再总结。树是很重要的非线性数据结构,在计算机领域的道理广泛的应用,程序员经常写程序后需要编译器编译运行,在编译程序的时候就用树形结构来表示源程序的语法结构,当然还有很多其它重要的应用,所以本章讲的树最一般本质的特征,在这个基础上会衍生出很多重要的应用。

树中最重要的是二叉树的存储结构及其各种操作,因为树也能转换为唯一的二叉树。所以主要总结二叉树的性质。

结点的度、树的度、层次、深度等基本概念就看书自己理解。二叉树的定义是:每个结点最多两颗子树,即不存在度大于2的结点,而且左右子树不能交换,是有序树。

二叉树的性质:好几个性质都是数值方面的,一般自己推也能推出来;满二叉树和完全二叉树等性质。

二叉树结构的定义:

typedef struct Bi{
	char data;
	int flag;//这个可以不定义
	Bi *lchild,*rchild;
}Bi,*Bihead;

上面是链式存储结构,还有其它的存储结构。

结构有了,那么我们假设二叉树也就有了,就需要对现在存在的一棵二叉树进行一些操作,主要的操作有:遍历,即访问二叉树中所有的结点,当然在我们人去查看二叉树中的结点肯定一目了然,但机器不行,所以就有先根序、中根序和后根序遍历二叉树等操作的存在。当然跳出这个三个遍历,其实最本质的还是广度优先遍历和深度优先遍历:深度 就是那三种遍历方法;广度就是层次遍历,即一层一层遍历二叉树结点。

首先广度遍历的三种遍历,如果使用递归的方法,则其实就是一个程序:

void show(Bihead b){//递归的遍历结点算法
	if(b!=NULL){
		cout<<b->data;//一般都是对结点进行操作的函数,这里简单的只是输出结点值
		show(b->lchild);
		show(b->rchild);
	}
}

上面是先根序遍历二叉树的递归算法,中根序和后根序就是把cout那句放到中间和最后,具体的就不详细讲了,还是读者自己去理解把,最好就是画图自己理解。

接下来是非递归的对二叉树进行遍历的算法:


void pre_in_order(Bihead b,stack &s){//非递归先根序和中根序遍历二叉树算法
	while(b||s.top!=s.base){
		if(b!=NULL){
			push(s,b);//
			cout<<b->data;//如果是先根序则这里输出
			b=b->lchild;
		}
		else{
			b=pop(s);//
			cout<<b->data;//如果是中根序 则是这里输出
			b=b->rchild;
		}
	}
}
自己写的堆栈结构:

typedef struct stack{
	Bihead *base,*top;
	int stacksize;
}stack;

void initStack(stack &s){
	s.base=new Bihead[100];
	s.top=s.base;
	s.stacksize=100;
}

void push(stack &s,Bihead h){
	*s.top=h;
	s.top++;
}

Bihead pop(stack &s){//
	if(s.top==s.base)
		return NULL;
	else
	{
		s.top--;
		return *s.top;
	}
}
Bihead gettop(stack &s){
	if(s.top==s.base)
		return NULL;
	else
		return *(s.top-1);
}

二叉树后根序非递归遍历算法有点难度,其实,只要理解了这三种遍历的本质就好了,因为非递归都是需要用到栈结构,可以 用STL的也可以自己写一个简单的,在遍历一个二叉树的时候,什么时候访问结点取决于第几次访问到这个结点,第一次就是先根序,第二次就是中根序,第三次就是后根序,举个例子吧,不然肯定不晓得我在说什么了,下图是个最简单的二叉树:

   9
  / \
2  3

这是个简单的二叉树,如果纯粹从深度遍历来看,应该是这样子的:922293339,这样的那本书上是有的,先根序则是结点第一次访问到,中根序是结点第二次访问到,第三次访问到结点是后根序,前两种遍历只要结点一次进栈:进栈前访问是先根序,出栈时候访问是中根序。而后根序则需要出栈后再次进栈,这里是难点,则后根序的遍历,可以先判断栈顶与当前访问结点的关系,如果是右子树与双亲结点,则需要出栈,否则不用出栈,将栈顶的结点的右子树赋予当前结点就行操作。

void post_order2(Bihead b,stack &s){
	Bihead fr=NULL;
	while(b||s.top!=s.base){
		if(b){
			push(s,b);
			b=b->lchild;
		}
		else{
			fr=gettop(s);
			if(!fr->rchild){
				b=pop(s);
				cout<<"out";
				fr=gettop(s);
				while(fr&&fr->rchild==b){//这里错误
					b=pop(s);
					cout<<" out";
					fr=gettop(s);
					if(!fr)
						break;
				}
				if(!fr)
					break;
				b=fr->rchild;
			}
			else{
				b=fr->rchild;//这里错误
			}
		}
	}
}
以上就是二叉树的深度优先遍历递归与非递归算法的实现,而到目前为止还没分析怎么来创建一棵二叉树,创建二叉树其实很简单,就是用先根序递归的方法进行创建,例如上面那个二叉树,在电脑上输入   92空格空格3空格空格 ,利用下面算法建立二叉树:

void create(Bihead &b){//
	char c;
	c=cin.get();
	if(c!=' '){
		b=new Bi;
		b->data=c;
		b->flag=0;
		b->lchild=NULL;
		b->rchild=NULL;
		create(b->lchild);
		create(b->rchild);
	}
}

以上就是二叉树初始化的操作,所以这里有一句话突然想到,先有计算机程序还是先有算法,我觉得是先有各种算法思想 ,然后计算机的各种软件都可以通过这种思想来实现。


上面讲的是二叉树遍历的深度优先,下面总结广度优先遍历,即层次遍历:

只需要把访问的当前访问的结点进入一个队列的结构(深度是利用了栈的结构);

void LevelTraverse(BinaryTreeNode * pRoot)
{
	if(pRoot == NULL)
		return;
	queue<BinaryTreeNode *> q;
	q.push(pRoot);
	while(!q.empty())
	{
		BinaryTreeNode * pNode = q.front();
		q.pop();
		Visit(pNode); // 访问节点
		if(pNode->m_pLeft != NULL)
			q.push(pNode->m_pLeft);
		if(pNode->m_pRight != NULL)
			q.push(pNode->m_pRight);
	}
	return;
}
上面这段代码是我拷贝的别人的,因为用到了队列结构,所以我们可以像上面一样,自己写一个队列结构及其操作,但其实STL中都有这些了,可以现成用就行。

以上只是二叉树最简单最基础的一些内容,更多的是以这个为基础的一些其它重要数据结构和算法,下面就简单总结一下:



1.最优二叉树,哈夫曼树,最优前缀码,这都是一个概念,这是利用二叉树来进行数据压缩的一种非常广泛和基础的方法,具体的介绍可以去看树上的内容,内容理解很简单,但是算法具体写出来稍稍有点难度,我自己也没有具体去实现。

2.计算机的文件系统是树的结构,比如Linux文件管理背景知识中所介绍的。在UNIX的文件系统中,每个文件(文件夹同样是一种文件),都可以看做是一个节点。非文件夹的文件被储存在叶节点。文件夹中有指向父节点和子节点的指针(在UNIX中,文件夹还包含一个指向自身的指针,这与我们上面见到的树有所区别)。在git中,也有类似的树状结构,用以表达整个文件系统的版本变。

其它具体的应用,以后再补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值