二叉树一样有两种存储方式,数组和链式存储,对于数组来讲,咱们利用二叉树的性质而后利用下标能够方便的找到一个节点的子节点和父节点。node
二叉树的性质:
1.二叉树的第i层上至多有2i-1个节点
2.深度为K的二叉树至多有2k-1个节点
3.任何一个二叉树中度数为2的节点的个数必度数为0的节点数目少1.
说明:度数为0,为叶子节点。
4.具备n个节点的彻底二叉树的深度为|_Log2N_|+1
5.若彻底二叉树中的某节点编号为i,则如有左孩子编号为2i,如有右孩子编号为2i+1,母亲节点为i/2。算法
结合第5条性质:数组
若彻底二叉树中的某节点编号为i,则如有左孩子编号为2i,如有右孩子编号为2i+1,母亲节点为i/2。数据结构
能够在这种彻底二叉树中十分方便的找到任何相关联(父子、兄弟等)的元素。post
可是因为顺序存储天生适配于彻底二叉树,对于下面这种非彻底二叉树并不合适,主要体如今空间上的浪费,因此咱们须要用到另外一种存储方式——链式存储。spa
在链式存储中,每一个节点的结构以下3d
结构描述:指针
一个存储数据的变量与两个指向孩子的指针域。code
利用指针域咱们即可以完美的存储非彻底二叉树,以下:blog
typedef struct BiTNode { TElemType data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree;
建立二叉树不是很容易的,而且它的建立方式也对应三种顺序。
其实二叉树的遍历和二叉树的建立能够说是几乎同样的过程。无非是建立的过程是给data赋值,遍历是取出data。
咱们先不给出建立的代码,而是要明白:
说明:
1.叶子节点不是没有左右孩子,只不过左右孩子都是空。
2.咱们的二叉树是手动输入数据进行建立的 我在实际开发中不多是这样建立的,因此咱们要处理空节点的问题。
3.建立和遍历的执行顺序(先序、中序、后序)是同样的,那么输入和取出的值才会是同样的。
好比咱们要先序建立一个上述图片中的二叉树,咱们应该怎样输入呢?
AB##CD##EF##G##
# 在这里是表示叶子节点的左或右节点为空。
代码以下:
void createBitree(Bitree &T) { char ch; if((ch=getchar())=='#') T=NULL; else { T=(Bitnode*)malloc(sizeof(Bitnode)); T->data=ch; createBitree(T->Lchild); createBitree(T->Rchild); } }
/*先序遍历*/ void preTraverse(Bitree T) { if(T!=NULL) { printf("%c",T->data); preTraverse(T->Lchild); preTraverse(T->Rchild); } }
/*中序遍历*/ void inorder(Bitree T) { if(T!=NULL) { inorder(T->Lchild); printf("%c",T->data); inorder(T->Rchild); } }
/*后序遍历*/ void postorder(Bitree T) { if(T!=NULL) { postorder(T->lchild); postorder(T->rchild); printf("%c",T->data); } }
►咱们输入字符后三种遍历状况以下:
AB##CD##EF##G##
先序:ABCDEFG
中序:BADCFEG
后序:BDFGECA
int Depth(Bitree T) {//返回深度 int d,dl,dr; if(!T) d=0; else { dl=Depth(T->Lchild); dr=Depth(T->Rchild); d=1+(dl>dr?dl:dr) ; } return d; }
queue<Bitree> TreeQueue; //使用队列 TreeQueue.push(tree); //先将队头元素加入队列 Bitree p = tree; while (!TreeQueue.empty()) //循环判断队列是否未空,若不空则 { p = TreeQueue.front(); //获取队列头节点,并出队列 TreeQueue.pop(); printf(" %c ", p->data); //打印队列元素 if (p->Lchild) //若是该节点有左节点 { TreeQueue.push(p->Lchild); //加入队列 } if (p->Rchild) //若是该节点有右节点 { TreeQueue.push(p->Rchild); //加入队列 } }
|说明:
1.算法轨迹:
依旧使用下图:
演示:
咱们首先将A加入队列, 此时对列 中只有 ⇐[A]
咱们将A出弹出队列,并将它的左右子树 BC加入队列,此时队列中有 ⇐[BC] ,打印出 A
咱们将B出弹出队列,它没有子树,咱们不作任何操做,此时队列中有 ⇐[C] ,打印出 ABC
咱们将C出弹出队列,并将它的左右子树 DE加入队列,此时队列中有 ⇐[DE] ,打印出 ABC
咱们将D出弹出队列,它没有子树,咱们不作任何操做,此时队列中有 ⇐[E] ,打印出 ABCD
咱们将E出弹出队列,并将它的左右子树 FG加入队列,此时队列中有 ⇐[FG] ,打印出 ABCDE
咱们将F出弹出队列,它没有子树,咱们不作任何操做,此时队列中有 ⇐[G] ,打印出 ABCDEF
咱们将G出弹出队列,它没有子树,咱们不作任何操做,此时队列中有 ⇐[null] ,打印出 ABCDEFG
结论:
根据轨迹咱们不难发现,队列是保证层序遍历的基础,由于它保证了先加入队列的上层元素会必后加入队列的下层元素优先出队列。
2.这段代码直接使用会出现问题,由于咱们使用了C++标准模板库中的queue。因此,咱们须要加入头文件
#include<queue>,而且要使用命名空间 using namespace std;。
3.咱们对待数据结构最重要的是理解和使用,实现它只是帮助咱们理解,咱们无须重复造轮子,在之后的算法中,咱们将尽量的引用标准模板库。