数据结构——栈和队列
1、第四章:栈和队列
通过下面的思维导图来依次分享「栈和队列」里面重要知识点的笔记。
2、第一节:树的基本特点
1. 树的定义:树是N(N≥0)个结点的有限集合,N=0时,称为空树,在任意一棵非空树中应满足:
①有且仅有一个特定的称为根的结点;
②当N>1时,其余结点可分为m(m>0)个互不相交的有限集合T1,T2,……,Tm,其中每一个集合本身又是一棵树,并且称为根结点的子树。
因此可看出:树的定义是递归的,是一种递归的数据结构,树作为一种逻辑结构,同时也是一种分层结构,其具有以下特点:
①树的根结点没有前驱结点,除根结点之外的所有结点有且只有一个前驱结点;
②树中所有结点可以有零个或多个后继结点。
2. 树中的基本术语:以下图为例可有
①根结点: A1即为该树的根结点。
②双亲结点: A2即为A5和A6的双亲结点。
③兄弟结点: 其中A5和A6互为兄弟结点,A2、A3和A4也互为兄弟结点。
④孩子结点: 其中A2的孩子结点即为A5或A6。
⑤子孙结点: 其中A1的子孙结点即为A5和A6。
⑥祖先结点: 其中A5和A6的祖先结点即为A1。
⑦结点的度:树中一个结点的子结点个数称为该结点的度。如A2的度为2,A1的度为3,。树中结点的最大度数称为树的度,即该树的度为3。
⑧分支结点:树中度大于0的结点称为分支结点(又称为非终端结点)。
⑨叶子结点:树中度为0的结点称为叶子结点(又称为终端结点)。
⑩结点的层次:从树根开始定义,根结点为第一层,它的子结点为第2层,以此类推。
⑪结点的深度:是从根结点开始自顶向下逐层累加。
⑫结点的高度:是从叶结点开始自底向上逐层累加。
⑬树的高度(又称为深度):是树中结点的最大层数。
3. 有序树和无序树:树中结点的子树从左到右是有次序的,不能交换,这样的树为有序树;有序树中,一个结点其子结点按从左到右顺序出现是有关联的。反之则称为无序树。
4. 路径和路径长度:树中两个结点之间的路径是由这两个结点之间所经过的结点序列构成的;而路径长度是路径上所经过的边的个数。
5. 森林:森林是m(m≥0)棵互不相交的树的集合。只要把树的根结点删去就变成了森林。反之,只要给n棵独立的树加上一个结点,并把这n棵树作为该结点的子树,则森林就变成了树。
6. 树的性质:
①树中的结点数等于所有结点的度数加1;
②度为m的树中第i层上至多有m^(i-1)个结点(i≥1);
③高度为h的m叉树至多有(m^h-1)/(m-1)个结点;
④具有n个结点的m叉树的最小高度为[logm(n(m-1)+1)];
3、第二节:二叉树的特点
1. 二叉树的定义:二叉树是另一种树形结构,其特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒,故二叉树是有序树。
二叉树递归形式的定义:二叉树是n(n≥0)个结点的有限集合:
①或者为空二叉树,即n=0;
②或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。
2. 二叉树与度为2的有序树的区别:
①度为2的树至少有3个结点,而二叉树可以为空;
②度为2的有序树的孩子结点的左右次序是相对于另一孩子结点而言的,如果某个结点只有一个孩子结点,这个孩子结点就无须区分其左右次序,而二叉树无论其孩子数是否为2,均需确定其左右次序,也就是说二叉树的结点次序不是相对于另一个结点而言,而是明确的。
3. 满二叉树:一棵高度为h,并且含有2^h-1个结点的二叉树称为满二叉树,即树中的每一层都含有最多的结点。满二叉树的叶子结点都集中在二叉树的最下一层,并且除叶子结点之外的每个结点度数均为2。如下图即为满二叉树:
4. 完全二叉树:设一个高度为h,有n个结点的二叉树,但且仅当其每一个结点都与高度为h的满二叉树中编号为1~n的结点一一对应时,称为完全二叉树。如下图中完全二叉树中结点编号与上图中满二叉树的结点编号可一一对应。
5. 二叉排序树:一棵二叉树或者是空二叉树,或者是具有如下性质的二叉树;左子树上所有结点的关键字均小于根结点的关键字;右子树上的所有结点的关键字均大于根结点的关键字。左子树和右子树又各是一棵二叉排序树。
6. 平衡二叉树:树上任一结点的左子树和右子树的深度之差不超过1。
7. 二叉树的性质:
①非空二叉树上叶子结点数等于度为2的结点数加1,即N0=N2+1。(说明,其中度为0,1,和2的结点个数分别为N0、N1和N2,故结点总数为N=N0+N1+N2);
②非空二叉树上第K层上至多有2^K-1个结点(K≥1);
③高度为H的二叉树至多有2^H-1个结点(H≥1);
④对于完全二叉树按从上到下、从左到右的顺序依次编号1,2,……,N,则当i>1且i为奇数时,其双亲结点的编号为(i-1)/2;当i>1且i为偶数时,其双亲结点的编号为i/2;
8. 二叉树的存储结构:
①二叉树的顺序存储结构:用一组地址连续的存储单元依次自上而下、自左而右存储完全二叉树上的结点元素,即将完全二叉树上编号为i的结点元素存储在某个数组下标为i-1的分量中,然后通过一些方法确定结点在逻辑上的父子和兄弟关系。如下图:
②二叉树的链式存储结构:用一个链表来存储一棵二叉树,二叉树中每一个结点用链表的一个链结点来存储。在二叉树中,结点结构通常包括若干数据域及若干个指针域。二叉链表至少包含3个域:数据域data、左指针域lchild和右指针域rchild。二叉树的链式存储结构伪代码描述如下:
typedef struct BiTNode{
ElemType data; //数据域
struct BiTNode *lchild *rchild; //左、右孩子指针
}BiTNode ,*BiTree;
具体将二叉树对应的二叉链表的存储结构如下图:
4、第三节:二叉树的遍历
二叉树的遍历是指按某条搜索路径访问树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。
1. 先序遍历(PreOrder):
操作过程如下:
如果二叉树为空,什么也不做。否则按下列顺序执行:
①访问根结点;
②先序遍历左子树;
③先序遍历右子树。
对应的递归算法如下:
void PreOrder(BiTree T){
if(T!=NULL){
visit(T); //访问根结点
PreOrder(T->lchild); //递归遍历左子树
PreOrder(T->rchild); //递归遍历右子树
}
}
2. 中序遍历(InOrder):
操作过程如下:
如果二叉树为空,什么也不做。否则按下列顺序执行:
①中序遍历左子树;
②访问根结点;
③中序遍历右子树。
对应的递归算法如下:
void InOrder(BiTree T){
if(T!=NULL){
InOrder(T->lchild); //递归遍历左子树
visit(T); //访问根结点
InOrder(T->rchild); //递归遍历右子树
}
}
3. 后序遍历(PostOrder):
操作过程如下:
如果二叉树为空,什么也不做。否则按下列顺序执行:
①后序遍历左子树
②后序遍历右子树;
③访问根结点。
对应的递归算法如下:
void PostOrder(BiTree T){
if(T!=NULL){
PostOrder(T->lchild); //递归遍历左子树
PostOrder(T->rchild); //递归遍历右子树
visit(T); //访问根结点
}
}
4. 层序遍历(LevelOrder):
操作过程如下:
按照二叉树中的层次,依次从上到下,且每一层从左至右依次访问二叉树中的每一个结点。
算法描述进行层次遍历需要借助一个队列。先将二叉树根结点入队,然后出队,访问该结点,如果它有左子树,则将左子树根结点入队;如果它有右子树,则将右子树根结点入队。然后出队,对出队结点访问,如此反复,直到队列为空。
二叉树的层次遍历算法伪代码如下:
void LevelOrder(BiTree T){
InitQueue(Q); //初始化辅助队列
BiTree p;
EnQueue(Q,T); //将根结点入队
while(!IsEmpty(Q)){ //队列不空循环
DeQueue(Q,p); //队头元素出队
visit(p); //访问当前p所指向结点
if(p->lchild!=NULL)
EnQueue(Q,p->lchild); //左子树不空,则左子树入队列
if(p->rchild!=NULL)
EnQueue(Q,p->rchild); //右子树不空,则右子树入队列
}
}
5. 由遍历序列构造二叉树:
①由二叉树的先序序列和中序序列可以难一地确定一 棵二叉树。
方法为:在先序遍历序列中,第一个结点一定是二叉树的根结点,而在中序遍历中,根结点必然将中序序列分割成两个子序列,前一个子序列就是根结点的左子树的中序序列,后一个子序列是根结点的右子树的中序序列。根据这两个子序列,在先序序列中找到对应的左子序列和右子序列。在先序序列中,左子序列的第一个结点是左子树的根结点,右子序列的第一个结点是右子树的根结点。如此递归地进行下去,便能唯一地确定这棵二叉树。
②由二叉树的后序序列和中序序列也可以唯一地确定一 棵二叉树,因为后序序列的最后一个结点就如同先房序列的第一个结点,可以将中序序列分割成两个子序列,然后采用类似的方法递归地进行划分,就可以得到一棵二叉树。
③由二叉树的层序序列和中序序列也可以唯一地确定一棵二叉树,但如果只知道二叉树的先序序列和后序序列,则已法唯-确定一棵二叉树。