二叉树及存储结构
3.2.1 二叉树的定义及性质
二叉树的定义
二叉树T:一个有穷的结点集合 这个集合可以为空 若不为空,则它是由根结点和称为其左子树TL和右子树TR的两个不相交的二叉树组成(L和R是下标)
二叉树具体五种基本形态
网络异常,图片无法展示
|
二叉树的子树有左右之分
网络异常,图片无法展示
|
特殊的二叉树
斜二叉树(Skewed Binary Tree)
- 只有左边或者只有右边,相当于一个链表
-
网络异常,图片无法展示|
完美二叉树(Perfect Binary Tree)
或者叫做满二叉树(Full Binary Tree)
网络异常,图片无法展示
|
特点: 一个深度为k(>=-1)且有2^(k+1) - 1个结点的二叉树称为完美二叉树。 满二叉树
完全二叉树(Complete Binary Tree)
有n个结点的二叉树,对树种结点按从上到下,从左到右的顺序进行编号,编号为i(1 <= i <= n)结点与满二叉树中编号为i结点在二叉树中位置相同 跟上方的区别就是除了最底下一层可以右边缺一点,上面跟满二叉树是一样的。最底下一层左边顺序不能乱
二叉树的几个重要性质
网络异常,图片无法展示
|
叶结点的总数等于有两个儿子的结点的总数加1
有一颗二叉树,其两个儿子的结点个数为15个,一个儿子的结点个数为32个,问该二叉树的叶结点个数是多少? 16
二叉树的抽象数据类型定义
类型名称:二叉树 数据对象集:一个有穷的结点集合。 若不为空,则由根结点和其左、右二叉子树组成。 操作集: BT属于BinTree,Item属于ElementType 重要操作有: 1.Boolean IsEmpty(BinTree BT):判别BT是否为空; 2.void Traversal(BinTree BT):遍历,按某顺序访问每个结点;(重要) 3.BinTree CreateBinTree():创建一个二叉树
常用的遍历方法
1.void PreOrderTraversal(BinTree BT)://先序--根、左子树、右子树 2.void InOrderTraversal(BinTree BT)://中序--左子树、根、右子树 3.void PostOrderTraversal(BinTree BT)://后序--左子树、右子树、根 4.void LevelOrderTraversal(BinTree BT)://层次遍历(层序遍历,从上到下、从左到右
3.2.2 二叉树的存储结构
1.顺序存储结构
完全二叉树:按从上至下,从左到右顺序存储n个结点的完全二叉树的结点父子关系; //这个树最适合数组方式解决
网络异常,图片无法展示
|
- 非根结点(序号 i > 1)的父节点的序号是[i/2]
- 这句话的意思就是说假设我们目前知道M结点时6,如果想知道它的父节点就是6/2=3,Q结点就是7/2=3.5,把小数去掉,父节点一样是3
- 结点(序号为i)的左孩子结点的序号是2i,(若2i <= n,否则没有左孩子)
- 举例:意思就是B左孩子是C,O的左孩子是M
- 结点(序号为i)的右孩子结点的序号是2i+1,(若2i+1 <= n,否则没有右孩子)
- 举例:类似上方的意思B右孩子是S...
一般二叉树也可以采用这种结构,缺点:但会造成空间浪费....(将缺少的结点补上一个空结点)
网络异常,图片无法展示
|
网络异常,图片无法展示
|
问题: 如果参照完全二叉树的表示方法用数组存储下面这棵二叉树,那么结点e所对应的数组下标是多少(树根下标为1)? 答案:6
网络异常,图片无法展示
|
2.链表存储
网络异常,图片无法展示
|
typedef struct TreeNode *BinTree; typedef BinTree Position; struct TreeNode{ ElementType Data; BinTree Left; BinTree Right; }
网络异常,图片无法展示
|
二叉树的遍历
3.3.1 先序中序后序遍历
二叉树的遍历
(1)先序遍历
遍历过程为: 1.访问根节点 2.先序遍历其左子树 3.先序遍历其右子树 对应的递归程序: void PreOrderTraversal(BinTree BT)//BT是树 { if( BT ){//看树是不是空的,空的就退出去,不空就访问根节点 printf("d",BT->Data); PreOrderTraversal(BT->Left);//指向左子树的那个根节点的地址进行递归 PreOrderTraversal(BT->Right);//指向右子树的那个根节点的地址进行递归 } }
网络异常,图片无法展示
|
先是从根开始 A(B D F E)(C G H I) 先序遍历=>A B D F E C G H I
(2)中序遍历
遍历过程为: 1.中序遍历其左子树 2.访问根结点; 3.中序遍历其右子树 对应的递归程序: void PreOrderTraversal(BinTree BT)//BT是树 { if( BT ){//看树是不是空的,空的就退出去,不空就访问根节点 PreOrderTraversal(BT->Left);//指向左子树的那个根节点的地址进行递归 printf("d",BT->Data); PreOrderTraversal(BT->Right);//指向右子树的那个根节点的地址进行递归 } }
网络异常,图片无法展示
|
中序遍历 => D B E F A G H C I (D B E F) A ( G H C I) 先是左边再右边,这里我原本的疑问是为什么是先E在F而不是先F再E 原因是E-F是一个树,然后因为这个是左子树,所以从左边开始,E在F的左边
网络异常,图片无法展示
|
(3)后序遍历
遍历过程为: 1.后序遍历其左子树 2.中序遍历其右子树 3.访问根结点 对应的递归程序: void PreOrderTraversal(BinTree BT)//BT是树 { if( BT ){//看树是不是空的,空的就退出去,不空就访问根节点 PreOrderTraversal(BT->Left);//指向左子树的那个根节点的地址进行递归 PreOrderTraversal(BT->Right);//指向右子树的那个根节点的地址进行递归 printf("d",BT->Data); } }
网络异常,图片无法展示
|
注意点:后序遍历,那当然得从下面开始咯,所以会发现B跟E是E优先。H跟C比是H优先 (D E F B)(H G I C) A 后序遍历 => D E F B H G I C A
总结
先序、中序和后序遍历过程:遍历过程中经过结点的路线一样,只是访问各结点的时机不同 图中先从入口到出口的曲线上用三种符号分别标记除先序,中序和后序的访问各结点时刻
网络异常,图片无法展示
|
3.3.2 中序非递归遍历
二叉树的非递归遍历
中序遍历非递归遍历算法
非递归算法实现的基本思路:使用堆栈
这里建议看视频的演示,非常形象
1.遇到一个结点,就把它压栈,并去遍历它的左子树; 2.当左子树遍历结束后,从栈顶弹出这个结点并访问它; 3.然后按其右指针再去中序遍历该结点的右子树 以下是完整代码实现演示 void InOrderTraversal(BinTree BT) { BinTree T = BT;//把BT赋给临时变量T Stack S = CreateStack(MaxSize);//创建并初始化堆栈 while(T || !IsEmpty(S) ){//树不空且堆栈不空 while(T){//判断堆栈空不空 //一直向左并将沿途结点压入堆栈 Push(S,T);//把T压入堆栈S中 T = T->Left;//然后把T往左挪,就是边挪边把结点压到堆栈里面去,压到底T为NULL就退出来 } if(!IsEmpty(S)){ T = Pop(S);//结点弹出堆栈 printf("%5d",T->Data);//(访问)打印结点 T = T->Right;//转向右子树 } } } ppoopoppoo错误 PPOPOOPPOO正确 第二次碰到同一个的时候就print出来
网络异常,图片无法展示
|
先序遍历的非递归遍历算法?
把上方那个算法中if中的printf("%5d",T->Data)移到while(T)中Push的后面