数据结构与算法之----树

来源:互联网 发布:java方法的定义和使用 编辑:程序博客网 时间:2024/06/06 10:39

二叉树的相关节点计算

1、具有n个节点的二叉树公有:(C2n下标,n上标)/(n+1)种。
2、若完全二叉树有m个节点,则其叶子节点的个数为:
没有单节点:则n+n-1=m
有一个单节点:n+n-1+1=m;
也就是说用m/2或者(m+1)/2
3、高度为k的完全二叉树最多有2k次方-1个节点。相等时为满二叉树。
最少有2(k-1)这种情况为,最底层只有1个节点,而上层有2(k-1)-1相加就能得到结果。
4、若高度数等于节点数,则是斜树,且前序和后序遍历相反。
5、赫夫曼编码相关,只要画出来看是否符合就可以。
6、具有n个值的赫夫曼节点数为:(n-1)*2+1
7、交换左右子树用后序遍历好一点。
8、n个节点的线索二叉树上含有的线索数为n+1
9、二叉树上叶子节点数等于双分支节点数+1
10、一棵具有1025个节点的二叉树高度为:11-1025
当一层只有一个时,为1025,当为完全二叉树时,为log2(n+1)向上取整,为11
11、算数表达式的前缀、中缀、后缀按照前序中序后序来处理就可以。


1、二叉树的创建、遍历、删除

代码如下:

#include "stdafx.h"#include<iostream>using namespace std;struct BitNode{int data;BitNode *lchild;BitNode *rchild;};void CreateTree(BitNode* &T)//这里传递的只能是指针,在这里如果不加上&的话,会出问题的,递归到最后回来的{//会是T=NULL这个地方还需要不断理解int a;cin>>a;if(a==8)//为空子树,这里定义{T=NULL;return;}T=new BitNode;//这个就是必须用到指针的地方,而且只能是前序遍历,没有树根,哪里来的子树T->data=a;//记得将左右孩子初始化+()CreateTree(T->lchild);CreateTree(T->rchild);}void PreOrderTraverse(BitNode *T){if(T){cout<<T->data;PreOrderTraverse(T->lchild);PreOrderTraverse(T->rchild);}}void InOrderTraverse(BitNode *T){if(T){InOrderTraverse(T->lchild);cout<<T->data;InOrderTraverse(T->rchild);}}void PostOrderTraverse(BitNode *T){if(T){PostOrderTraverse(T->lchild);PostOrderTraverse(T->rchild);cout<<T->data;}}void DeleteTree(BitNode *T)//只能用后序遍历{if(T){if(T->lchild)DeleteTree(T->lchild);if(T->rchild)DeleteTree(T->rchild);cout<<T->data;delete T;}}int _tmain(int argc, _TCHAR* argv[]){BitNode *T=NULL;CreateTree(T);PreOrderTraverse(T);cout<<'\n';InOrderTraverse(T);cout<<'\n';PostOrderTraverse(T);cout<<'\n';DeleteTree(T); return 0;}


输出:

0 1 8 8 2 3 8 8 8
0123
1032
1320
1320请按任意键继续. . .

总结:

(1)当形参需要new来返回的时候,用指针,而且用引用指针,如 int* &a;  否则会出问题,,也就是说指针指向的内容也需要绑定??值得研究??

理解为:传递指针,形参执行构造函数,然而只有用引用时,才能改变原来指针的内容,故为指针 引用。

(2)树的建立只能用前序建立法,因为只有当根创建了,子树才能建立

(3)树的删除必须用后序遍历,只有子树删除了,根才能够删除

(4)示例化对象时,最好调用默认构造函数进行初始化,当使用new的时候会调用系统默认构造函数帮你进行初始化,这就是使用new的一个好处。

(5)也就是说,new能够动态分配内存,还能够调用系统默认构造函数进行初始化,只需要加上()即可。

(6)传递指针的时候,要初始化指针,因为执行复制构造函数。

 

1.1、前序、中序、后序遍历的非递归实现

(1)前序的非递归实现,利用一个堆栈,每一次处理,打印本节点,将右孩子压入堆栈,指针指向左节点,循环while

(2)中序遍历的非递归实现,每一次处理,先压入右节点,再压入本节点(标记处理过),指向左孩子,while循环,直到左边遍历完毕

循环出来之后,while循环不断出堆栈,若出来的点已经被访问过,则打印该点,否则,就是原来的右节点,出栈,调用上面的函数。感觉需要跳转的样子。

解决办法:同样只要存储自己,出栈的时候只需要将右节点做处理即可。也就是判断若有右孩子,则跳出,进入do while 

(3)后序遍历需要压入的同样有两个,先压入自己(处理过),再压入右节点,指向左节点

解决办法:书上的方法是,利用在两个循环外面做一个do while  实现跳转,中间用 flag标志来控制。  另外 不需要压入两个点,只需要压入自己就可以了。判断若上一个处理的点(用p缓存)是自己的右孩子,或者是空的,那么就可以出栈了。

 

2、统计树节点的个数

int CountNum(BitNode* T){if(T){return( CountNum(T->lchild)+CountNum(T->rchild)+1);}elsereturn 0;}

思想:

(1)cvte笔试的时候没有做出来,其实是非常简单的,所以还是要多练习

(2)要想求子节点的个数,对于一个小堆,就是返回本生以及做孩子下面的节点树,以及右孩子下面的节点树。

(3)递归主体 返回左孩子+右孩子的+本身(1)

(4)递归出口 若本身为空,返回(0)

0 0
原创粉丝点击