1.22k likes | 1.44k Views
树. 重点. 树的基本概念和术语 二叉树的概念及性质 二叉树的存储结构 顺序存储 链式存储 遍历二叉树 递归算法 非递归算法 线索二叉树 树的应用:哈夫曼树. 7.1 树的概念和性质. 树的定义( P85 ) 树: n ( n ≥0) 个 结点 的有限 集合 。当 n =0 时,称为空树;任意一棵非空树满足以下条件: ⑴ 有且仅有一个特定的称为 根 的结点;
E N D
重点 • 树的基本概念和术语 • 二叉树的概念及性质 • 二叉树的存储结构 • 顺序存储 • 链式存储 • 遍历二叉树 • 递归算法 • 非递归算法 • 线索二叉树 • 树的应用:哈夫曼树
7.1 树的概念和性质 • 树的定义(P85) 树:n(n≥0)个结点的有限集合。当n=0时,称为空树;任意一棵非空树满足以下条件: • ⑴ 有且仅有一个特定的称为根的结点; • ⑵ 当n>1时,除根结点之外的其余结点被分成m(m>0)个互不相交的有限集合T1,T2,… ,Tm,其中每个集合又是一棵树,并称为这个根结点的子树。 • 树的定义是采用递归方法
树的形式定义 • D={ai | ai∈ElemSet,i=1,…,n} • 二元关系S的定义: • 当n=1时,S=φ; • 当n>1时:
树的基本术语(P86) • 结点的度:结点所拥有的子树的个数。 • 叶子结点:度为0的结点,也称为终端结点。 • 分支结点:度不为0的结点,也称为非终端结点。 • 树的度:树中各结点度的最大值。 • 孩子、双亲:树中某结点子树的根结点称为这个结点的孩子结点,这个结点称为它孩子结点的双亲结点; • 兄弟:具有同一个双亲的孩子结点互称为兄弟。 • 路径:如果树的结点序列n1, n2, …, nk有如下关系:结点ni是ni+1的双亲(1<=i<k),则把n1, n2, …, nk称为一条由n1至nk的路径;路径上经过的边的个数称为路径长度。
树的基本术语(续) • 祖先、子孙:在树中,如果有一条路径从结点x到结点y,那么x就称为y的祖先,而y称为x的子孙。 • 结点所在层数:根结点的层数为1;对其余任何结点,若某结点在第i层,则其孩子结点在第i+1层。 • 树的深度:树中所有结点的最大层数,也称高度。 • 有序树、无序树:如果一棵树中结点的各子树从左到右是有次序的,称这棵树为有序树;反之,称为无序树。 • 森林:m (m≥0)棵互不相交的树的集合。
7.2 二叉树的概念和性质 • 研究二叉树的意义? • 二叉树的结构相对简单,其运算也自然简单,便于初学者入门。 • 由于多叉树可以借助一定的规则转换为二叉树,因此二叉树结构在应用中具有非常重要的地位。
7.2 二叉树的概念和性质 • 二叉树的定义(P88) • 二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
7.2 二叉树的概念和性质 • 二叉树的特点 • 每个结点的度只可能是0,1,2; • 二叉树是有序树,即使某结点只有一棵子树,也要区分该子树是左子树还是右子树。
7.2 二叉树的概念和性质 • 二叉树的5种基本形态(P89)
7.2 二叉树的概念和性质 例:画出具有3个结点的树和具有3个结点的二叉树的形态 • 二叉树和树是两种树结构。
7.2 二叉树的概念和性质 • 特殊的二叉树 • 满二叉树 • 在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。 • 满二叉树的特点: • 叶子只能出现在最下一层; • 只有度为0和度为2的结点。 • 完全二叉树:深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树从编号1到n的结点一一对应是时,称为完全二叉树。
7.2 二叉树的概念和性质 p89 • 性质1:在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有: n0=n2+1。 • 性质2:二叉树的第i层上最多有2i-1个结点(i≥1)。 • 性质3:一棵深度为k的二叉树中,最多有2k-1个结点,最少有k个结点。
性质4 具有n个结点的完全二叉树的深度为 log2n +1。 • 性质5对一棵具有n个结点的完全二叉树中从1开始按层序编号,则对于任意的序号为i(1≤i≤n)的结点(简称为结点i),有: • (1)如果i>1,则结点i的双亲结点的序号为 i/2;如果i=1,则结点i是根结点,无双亲结点。 • (2)如果2i≤n,则结点i的左孩子的序号为2i;如果2i>n,则结点i无左孩子。 • (3)如果2i+1≤n,则结点i的右孩子的序号为2i+1如果2i+1>n,则结点 i无右孩子。 7.2 二叉树的概念和性质
7.2 二叉树的概念和性质 性质六:给定n个结点,能构成H(n)种不同的二叉树:
考研真题 • 具有3个结点的二叉树有( )种 • 有10个叶子结点的二叉树中有( )个度为2的结点。 • 二叉树有10个度为2的结点,5个度为1的结点,则度为0的结点个数是( ) • 一棵完全二叉树有1001个结点,其中叶子结点的个数是( ) • 高度为4的完全二叉树至少有( )个结点 • 高度为5的完全二叉树至多有( )个结点
7.3.1 二叉树的顺序存储结构(P91) • 二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置(下标)应能体现结点之间的逻辑关系——父子关系。 • 如何利用数组下标来反映结点之间的逻辑关系? • 二叉树的性质5为二叉树的顺序存储指明了存储规则:依照完全二叉树的结点编号次序,依次存放各个结点。 • 注意:C/C++中数组的起始地址为0,编号为i的结点存储在下标为i1的单元内。 • 完全二叉树和满二叉树中结点的序号可以唯一地反映出结点之间的逻辑关系 。
7.3.1 二叉树的顺序存储结构 #define MAX_TREE_SIZE 100 //为最大结点数 Typedef Telemtype SqBitree(MAX_TREE_SIZE);//0号单元存储根结点, SqBitree bt;
7.3.1 二叉树的顺序存储结构 • 非完全二叉树修补结构不存在的结点,用特殊符号标识。
7.3.1 二叉树的顺序存储结构 • 顺序存储结构适用于? • 完全二叉树 • 在最坏的情况下,一个深度为K且只有K个结点的单支树,树中不存在度为2的结点,却需要长度为2k-1的一维数组
lchild data rchild 7.3.2 二叉树的链式存储结构(P92) • 基本思想:令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子的指针。 结点结构: 其中,data:数据域,存放该结点的数据信息; lchild:左指针域,存放指向左孩子的指针; rchild:右指针域,存放指向右孩子的指针。
lchild data rchild 左孩子结点 右孩子结点 二叉链表结构定义 struct BiNode { T data; // 结点数据 BiNode<T> *lchild; // 左孩子的指针 BiNode<T> *rchild; // 右孩子的指针 };
A A C B B C D E ∧ ∧ F D F E ∧ ∧ G ∧ G ∧ 具有n个结点的二叉链表中,有多少个空指针? 7.3.2 二叉树的链式存储结构 ∧ ∧
A A C B B C D E ∧ ∧ F D F E ∧ ∧ G ∧ G ∧ 7.3.2 二叉树的链式存储结构 ∧ ∧ 具有n个结点的二叉链表中,有n+1个空指针。
前序遍历 中序遍历 后序遍历 层序遍历 抽象操作,可以是对结点进行的各种处理,这里简化为输出结点的数据。 非线性结构线性化 7.4 二叉树的遍历 二叉树的遍历是指从根结点出发,按照某种次序访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。 二叉树遍历操作的结果?
根结点D 左子树L 右子树R 二叉树 7.4 二叉树的遍历 考虑二叉树的组成: 如果限定先左后右,则二叉树遍历方式有三种: 前序:DLR 中序:LDR 后序:LRD 层序遍历:按二叉树的层序编号的次序访问各结点。
A B C D E F G 7.4 二叉树的遍历 前序遍历的概念 ①若二叉树为空,则空操作返回; (否则) ②访问根结点; ③前序遍历根结点的左子树; ④前序遍历根结点的右子树。 前序遍历序列:A B D G C E F
前序遍历——递归算法 Void trave (BTNode *p) { if (p!=Null) visit(p); Trave (p->lchild); Trave (p->rchild); }// 假设 visit()已定义过,包含对结点P的各种访问操作,如打印出P对应的数值 // States printelem( Telemtype e) { Print(e); return OK; }
A B C D E F G 7.4 二叉树的遍历 中序遍历的概念 ①若二叉树为空,则空操作返回; (否则) ②中序遍历根结点的左子树; ③访问根结点; ④中序遍历根结点的右子树。 中序遍历序列:D G B A E C F
中序遍历——递归算法 void inorder (BTNode *p) { if (p==NULL) return; // ① InOrder(p->lchild); // ③ cout << p->data; // ② InOrder(p->rchild); // ④ }
A B C D E F G 7.4 二叉树的遍历 后序遍历的概念 ①若二叉树为空,则空操作返回; (否则) ②后序遍历根结点的左子树; ③后序遍历根结点的右子树。 ④访问根结点; 后序遍历序列:G D B E F C A
后序遍历——递归算法 Void PostOrder (BTNode *p) { if (p==NULL) return; // ① PostOrder(p->lchild); // ③ PostOrder(p->rchild); // ④ cout << p->data; // ② } Void trave (BTNode *p) { if (p!=Null) {// (1) Trave (p->lchild); // (2) Trave (p->rchild); // (3) }//假设 visit()已定义过, visit(p);放在不同的位置,表示不同的遍历方式。
Void inorder(BTNode *bt) { BTNode *stack[maxsize];Int top=-1; BTNode *p; If(bt!=NULL) { p=bt; While (top!=-1|| P!=NULL) { while(P!=NULL)//左孩子存在,则左孩子入栈,即各层结点的所有左孩子进栈 { stack[++top]=p; p=p->lchild; } If(top!=-1) //在栈不空的情况下出栈左孩子 { p=stack[top--]; cout <<p->data<<" "; p=p->rchild; } } } } 7.4 二叉树的非递归遍历 • 中序遍历二叉树的非递归算法: • 指向根结点的指针,当栈顶记录中的指针非空时,应遍历左子树,即指向左子树根的指针进栈 • 若栈顶记录中的指针值为空,则应退到上一层,若是从左子树返回,则应访问当前层,即栈顶记录中指针所指的根结点; • 若是从右子树返回,则表明当前层的遍历结束,则应继续退栈。 • 思想: • 开始根结点入栈, • 循环执行如下操作:如果栈顶结点左孩子存在,则左孩子入栈,如果栈顶结点左孩子不存在,则出栈并输出栈顶结点,然后检查其右孩子是否存在,如果存在,则右孩子入栈。 • 当栈空时且已遍历完时算法结束。
7.4 二叉树的非递归遍历 • 用自己申请的栈来代替系统栈,先序遍历: Void preorder(BTNode *bt) { BTNode *stack[maxsize]; Int top=-1; BTNode *p; If(bt!=NULL) { stack[++top]=bt; //根结点先入栈 While (top!=-1) { p=stack[top--]; cout <<p->data<<" ";// 出栈并输入栈点结点 If (p->rchild!=NULL) { stack[++top]= p->rchild; } If (p->lchild!=NULL) { stack[++top]= p->lchild; } } } }
A B C D E F G 7.4 二叉树的遍历 层次遍历的概念 二叉树的层次遍历是指从二叉树的第一层(即根结点)开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。 层序遍历序列:A B C D E F G
二叉树的层次遍历算法 • 队列Q初始化; • 2. 如果二叉树非空,将根指针入队; • 3. 循环直到队列Q为空 • 3.1 p=队列Q的队头元素出队; • 3.2 访问结点p的数据域; • 3.3 若结点p存在左孩子,则将左孩子指针入队; • 3.4 若结点p存在右孩子,则将右孩子指针入队;
A B C D E F G 7.4 二叉树的遍历 层次遍历 A B C D E F G 遍历序列: A B C G D E F
层次遍历——非递归算法 void level(BTNode *p) // 层次遍历二叉树 { int front, rear; BTNode *que[maxsize] // 定义一个循环队列,用于记录将要访问的层次上的结点 front=rear=0; //初始化循环队列 BTNode *q; If(p!=Null) { rear=(rear+1)%maxsize; Que[rear]=p; //根结点入队 While(front!=rear) //当队列为空时循环 { front=(front+1)%maxsize; Q=que[front];//队头结点出队 Visit(q); If(q->lchild!=Null) { rear=(rear+1)%maxsize; Que[rear]=q->lchild; } If(q->rchild!=Null) { rear=(rear+1)%mazsize; Que[rear]=q->rchild; } } } }
若已知一棵二叉树的前序(或中序,或后序,或层序)序列,能否唯一确定这棵二叉树呢?若已知一棵二叉树的前序(或中序,或后序,或层序)序列,能否唯一确定这棵二叉树呢? A B C A B C 二叉树的遍历的应用 例:已知前序序列为ABC,则可能的二叉树有5种。
若已知一棵二叉树的前序序列和后序序列,能否唯一确定这棵二叉树呢?若已知一棵二叉树的前序序列和后序序列,能否唯一确定这棵二叉树呢? A B C A B C 例:已知前序遍历序列为ABC,后序遍历序列为CBA,则下列二叉树都满足条件。
若已知一棵二叉树的前序序列和中序序列,能否唯一确定这棵二叉树呢?怎样确定?若已知一棵二叉树的前序序列和中序序列,能否唯一确定这棵二叉树呢?怎样确定? 例如:已知一棵二叉树的前序遍历序列和中序遍历序列分别为ABCDEFGHI 和BCAEDGHFI,如何构造该二叉树呢?
A B C D E F G H I A B D C E FG HI 前序:A B C D E F GH I中序:B C A E D G H F I 前序:B C 中序:B C 前序: D E F G H I 中序: E D G H F I
A B D C E F A G I B D H C E FG HI 前序:F GH I 中序:G H F I 前序: D E F G H I 中序: E D G H F I
试题例: 试找出分别满足下列条件的所有二叉树 ①先序序列和中序序列相同 ②中序序列和后序序列相同 ③先序序列和后序序列相同 ④前序和后序遍历正好相反: 根据二叉树的前序和中序、中序和后序两对遍历序列都可以唯一确定这棵二叉树。
考研真题: • 一棵二叉树的先序遍历序列是A,B,C,D,E,F,中序遍历序列为C,B,A,E,D,F,则后序遍历序列为: C,B,E,F,D,A
E F D G A B / + + * - C * 考研真题3 1.已知一算术表达式的中缀形式为 A+B*C-D/E,后缀形式为ABC*+DE/-,其前缀形式为( ) 【北京航空航天大学 1999 一、3 (2分)】 2.算术表达式a+b*(c+d/e)转为后缀表达式后为( )【中山大学 1999 一、 3. 设有一表示算术表达式的二叉树(见下图),【南京理工大学1999 一、20(2分)】它所表示的算术表达式是( ) 4. 设树T的度为4,其中度为1,2,3和4的结点个数分别为4,2,1,1 则T中的叶子数为( )【南京理工大学 2000 一、8 (1.5分)】 5. 在下述结论中,正确的是( )【南京理工大学 99 一4 (1分) ①只有一个结点的二叉树的度为0; ②二叉树的度为2; ③二叉树的左右子树可任意交换; ④深度为K的完全二叉树的结点个数小于或等于深度相同的满二叉树 A.①②③ B.②③④ C.②④ D.①④
考研真题4 27. 利用二叉链表存储树,则根结点的右指针是( )。【青岛大学 2001 五、5 (2分)】 A.指向最左孩子 B.指向最右孩子 C.空 D.非空 28.对二叉树的结点从1开始进行连续编号,要求每个结点的编号大于其左、右孩子的编号,同一结点的左右孩子中,其左孩子的编号小于其右孩子的编号,可采用( )次序的遍历实现编号。【北京理工大学 2000 一、4 (2分)】 A.先序 B. 中序 C. 后序 D. 从根开始按层次遍历 29.树的后根遍历序列等同于该树对应的二叉树的( ). 【北京理工大学 2001 六、6 (2分)】 A. 先序序列 B. 中序序列 C. 后序序列
考研真题5 • 4. 设有一棵算术表达式树,用什么方法可以对该树所表示的表达式求值? • 【中国人民大学2001二、3(4分)】 • 5.将算术表达式((a+b)+c*(d+e)+f)*(g+h)转化为二叉树。【东北大学 2000 三、1 (4分)】
小结 • 树的基本概念和术语 • 二叉树的概念 • 二叉树的性质 • 二叉树的存储:顺序、链式 • 二叉树的遍历