1、数据结构程序员考试 第六章 树和二叉树第六章 树和二叉树6.1 树的类型定义 数据对象D:D是具有相同特性的数据元素的集合。 数据关系R: 若D为空集,则称为空树; 否则:(1) 在D中存在唯一的称为根的数据元素root, (2) 当n1时,其余结点可分为m(m0)个互不相交的有限集 T1, T2, , Tm, 其中每一棵子集本身又是一棵符合本定义的树,称为根root的子树。 基本操作:查找: Root(T); Value(T, cur_e); Parent(T, cur_e); LeftChild(T, cur_e); RightSibling(T, cur_e); TreeEmpty(T)
2、; TreeDepth(T); TraverseTree(T, Visit();插入: InitTree(&T); CreateTree(&T, definition); Assign(T, cur_e, value); InsertChild(&T, &p, i, c);删除: ClearTree(&T); DestroyTree(&T); DeleteChild(&T, &p, i); DestroyTree(&T);有向树 : ) 有确定的根; ) 树根和子树根之间为有向关系有序树和无序树的区别在于: 子树之间是否存在次序关系? 和线性结构的比较 线性结构 树结构第一个数据元素 根结点
3、(无前驱) (无前驱)最后一个数据元素 多个叶子结点 (无后继) (无后继)其它数据元素 树中其它结点(一个前驱、一个后继) (一个前驱、多个后继)基本术语结点:数据元素 + 若干指向子树的分支结点的度:分支的个数树的度:树中所有结点的度的最大值叶子结点:度为零的结点分支结点:度大于零的结点从根到结点的路径:孩子结点、双亲结点、兄弟结点、祖先结点、子孙结点结点的层次:假设根结点的层次为1, 第l层的结点的子树根结点的层次为l+1树的深度:树中叶子结点所在的最大层次森林:是m(m0)棵互不相交的树的集合任何一棵非空树是一个二元组Tree = (root,F)其中: root被称为根结点,F被称为
4、子树森林6.2 二叉树的类型定义 二叉树或为空树;或是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。 二叉树的五种基本形态: 二叉树的主要基本操作:查找: Root(T); Value(T, e); Parent(T, e); LeftChild(T, e); RightChild(T, e); LeftSibling(T, e); RightSibling(T, e); BiTreeEmpty(T); BiTreeDepth(T); PreOrderTraverse(T, Visit(); InOrderTraverse(T, Visit(); PostOrderTra
5、verse(T, Visit(); LevelOrderTraverse(T, Visit();插入: InitBiTree(&T); Assign(T, &e, value); CreateBiTree(&T, definition); InsertChild(T, p, LR, c);删除: ClearBiTree(&T); DestroyBiTree(&T); DeleteChild(T, p, LR); 二叉树的重要特性:性质 1 : 在二叉树的第 i 层上至多有2i-1个结点 (i1)性质 2 : 深度为k的二叉树上至多含2k-1个结点 (k1)性质 3 : 对任何一棵二叉树,若他含
6、有n0个叶子结点、n2个度为2的结点,则必存在关系式: n0 = n2+1 两类特殊的二叉树:满二叉树:指的是深度为k且含有2k-1个结点的二叉树完全二叉树:树中所含的n个结点和满二叉树中编号为1至n的结点一一对应性质 4 : 具有n个结点的完全二叉树的深度为 log2n+1性质 5 : 若对含n个结点的二叉树从上到下且从左至右进行1至n的编号,则对二叉树中任意一个编号为i的结点:(1) 若i=1,则该结点是二叉树的根,无双亲, 否则,编号为i/2的结点为其双亲结点;(2) 若2in,则该结点无左孩子, 否则,编号为2i的结点为其左孩子结点;(3) 若2i+1n,则该结点无右孩子结点, 否则,
7、编号为2i+1的结点为其右孩子结点。6.3 二叉树的存储结构一、 二叉树的顺序存储表示 class BiTree private: Array SqBiTree;显然,这种顺序存储结构仅适用于完全二叉树。因为,在最坏的情况下,一个深度为 k 且只有 k 个结点的单支树(树中不存在度为 2 的结点)却需要长度为2k-1的一维数组。二、二叉树的链式存储表示 1. 二叉链表template class BiTreeNode / 二叉树的结点 protected: BiTreeNode *lchild; / 指向左子树的指针BiTreeNode *rchild; / 指向右子树的指针 public:
8、Elem* data; / constructorTreeNode (const Elem& item, BiTreeNode *lptr = NULL, BiTreeNode *rptr = NULL); /virtual destructor. needed for AVL tree class virtual TreeNode(void); / access methods for the pointer fields BiTreeNode* Left(void) const; BiTreeNode* Right(void) const; void AssignLeft(TreeNode
9、* p); void AssignRight(TreeNode* p); / BinSTree needs access to left and right friend class BinSTree; 2三叉链表 protected: BiTreeNode *lchild; / 指向左子树的指针BiTreeNode *rchild; / 指向右子树的指针BiTreeNode *parent; / 指向双亲的指针 3双亲链表 protected: BiTreeNode *parent; / 指向双亲的指针 char LRTag; / 指示左右的标志 4线索链表6.4 二叉树的遍历一、问题的退出
10、顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。“访问”的含义可以很广,如:输出结点的信息等。对“二叉树”而言,可以有三条搜索路径:1先上后下的按层次遍历;2先左(子树)后右(子树)的遍历;3先右(子树)后左(子树)的遍历。二、先左后右的遍历算法 先(根)序的遍历算法: 若二叉树为空树,则空操作;否则,(1)访问根结点;(2)先序遍历左子树;(3)先序遍历右子树。 中(根)序的遍历算法: 若二叉树为空树,则空操作;否则,(1)中序遍历左子树;(2)访问根结点;(3)中序遍历右子树。 后(根)序的遍历算法: 若二叉树为空树,则空操作;否则,(1)后序遍历左子树;
11、(2)后序遍历右子树;(3)访问根结点。三、算法的递归描述/ preorder recursive scan of the nodes in a tree. template void Preorder (TreeNode *t, void visit(Elem& item) if (t != NULL) visit(t-data); / 访问结点 Preorder(t-Left(), visit); / 遍历左子树 Preorder(t-Right(), visit); / 遍历右子树 四、中序遍历算法的非递归描述/ return address of last node on the le
12、ft branch / from t, stacking nodes on the way. used for / iterative inorder scan.template TreeNode *GoFarLeft(TreeNode *t, StackTreeNode *& S) if (t = NULL) return NULL; while (t-Left( ) != NULL) S.Push(t); t = t-Left(); return t;/ inorder iterative scantemplate void Inorder_I(TreeNode *t, void visi
13、t(Elem& c) StackTreeNode* S; t = GoFarLeft(t,S); / continue until t is NULL while(t != NULL) visit(t-data); if (t-Right( ) != NULL) t = GoFarLeft(t-Right( ),S); else if ( !S.StackEmpty( ) t = S.Pop(); else t = NULL; / we are done 五、遍历算法的应用举例:1、统计二叉树中叶子结点的个数/ 先序遍历template void CountLeaf (TreeNode *t,
14、 int& count) if (t != NULL) if (t-Left( ) = NULL & t-Right( ) = NULL) count+; CountLeaf(t-Left( ), count); CountLeaf(t-Right( ), count); 2、求二叉树的深度(后序遍历)template int Depth (TreeNode *t) int depthLeft, depthRight, depthval; if (t = NULL) depthval = 0; else depthLeft = Depth(t-Left( ); depthRight= Dept
15、h(t-Right( ); depthval = 1 +(depthLeft depthRight? depthLeft:depthRight); return depthval;3、复制二叉树/ 生成一个二叉树的结点template TreeNode *GetTreeNode(Elem item, TreeNode *lptr = NULL, TreeNode *rptr = NULL) TreeNode *p; p = new TreeNode (item, lptr, rptr); if (p = NULL) exit(1); return p;template TreeNode *Co
16、pyTree(TreeNode *t) TreeNode *newlptr, *newrptr, *newnode; if (t = NULL) return NULL; if (t-Left( ) != NULL) newlptr = CopyTree(t-Left( ); else newlptr = NULL; if (t-Right( ) != NULL) newrptr = CopyTree(t-Right( ); else newrptr = NULL; newnode = GetTreeNode(t-data, newlptr, newrptr); return newnode;
17、4、建立二叉树的存储结构TreeNode* crtTree(char *str, int& i) / 建立由字符串stri.n定义的二叉树,stri为根 TreeNode* t; char Ch = stri; if (Ch = ) t = NULL; else t = GetTreeNode(Ch); / creat the root node i+; t-AssignLeft(crtTree(str, i); i+; t-AssignRight(crtTree(str, i); return t; 建表达式的树由先缀表示式建树由原表达式建树 a+bc(de/f)gTreeNode* crt
18、Tree(char *str) TreeNode* t; StackTreeNode* SD; Stack SP; SP.Push(); p = str; ch = *p; while(!SP.StackEmpty() if (ch 是字母) 建叶子结点t;SD.Push(t); else switch (ch) case (: SP.Push(ch); break;case ): c=SP.Pop(); while (c!=() 建二叉树t;SD.Push(t); c=SP.Pop(); break; defult: while(!SP.Gettop(c)&(precede(c, ch) c
19、=SP.Pop();建二叉树t;SD.Push(t); if (ch!=) SP.Push(ch); else c=SP.Pop(); break; / defult / switch / else if (ch!=) p+; ch=*p; / while t=SD.Pop(); return t; / crtTree由表达式的先缀和中缀表示式建树 a b c d e f ga b c d e f g6.5 线索二叉树一、 何谓线索二叉树?在存储结构中保存遍历所得“前驱”和“后继”的信息线索链表: 对二叉链表的结点增加两个标志域,并作如下规定:若该结点的左子树不空,则lchild域的指针指向其
20、左子树,且左标志域的值为0; 否则,lchild域的指针指向其“前驱”,且左标志的值为1.若该结点的右子树不空,则rchild域的指针指向其右子树,且右标志域的值为0; 否则,rchild域的指针指向其“后继”,且右标志的值为1. 线索链表的结点类:template class ThrNode protected:PointerTag ltag;PointerTag rtag;ThrNode lchild;TheNode rchild; 线索链表的遍历算法: for ( p = firstNode(T); p; p = Succ(p) ) Visit(p);中序线索化链表的遍历算法: 中序遍历
21、的第一个结点? 在中序线索化链表中结点的后继?template void InOrderTraverse_Thr(ThrNode *T, void Visit(Elem& item) / t指向头结点,头结点的左链lchild指 / 向根结点,头结点的右链rchild指向中 / 序遍历的最后一个结点。 / 中序遍历二叉线索链表表示的二叉树T。 p = T-Left(); / p指向根结点 while (p != T) / 空树或遍历结束时,p=T while (p-LTG() = Link) p = p-Left();Visit(p-data); / 访问其左子树为空的结点 while(p-R
22、TG()=Thread&p-Right()!= T) p = p-Right(); Visit(p-data); / 访问后继结点 p = p-Right(); / p进至其右子树根 / InOrderTraverse_Thr 如何建立线索链表?在中序遍历过程中保存当前访问结点的“前驱”和“后继”信息template ThrNode* InOrderThreading(ThrNode* T) / 中序遍历二叉树T,并对其进行中序线 / 索化,Thrt指向线索化之后的头结点。 ThrNode* Thrt; Thrt = GetThrNode( , Link, Thread); / 建头结点 Th
23、rt-AssignRight( Thrt); / 右指针回指 if (T = NULL) Thrt-AssignLeft ( Thrt); / 若二叉树空,则左指针回指 else Thrt-AssignLeft(T); pre = Thrt;InThreading(T); /中序遍历进行中序线索化 pre-AssignRight(Thrt); pre-AssignRTag(Thread); / 最后一个结点线索化 Thrt-AssignRight (pre); return Thrt; / InOrderThreading附设指针pre,并始终保持指针pre指向当前访问的、指针p所指结点的前驱
24、template void InThreading(ThrNode* p) if (p) InThreading(p-Left(); / 左子树线索化 if (!p-Left( ) p-AssignLTag(Thread); p-AssignLeft(pre); / 建前驱线索 if (!pre-Right( ) pre-AssignRTag(Thread); pre-AssignRight(p); / 建后继线索 pre = p; / 保持pre指向p的前驱 InThreading(p-Right(); / 右子树线索化 / InThreading6.6 树和森林的表示方法树的三种存储结构一
25、、双亲表示法: #define MAX_TREE_SIZE 100结点结构: typedef struct PTNode Elem data; int parent; / 双亲位置域 PTNode;树结构: typedef struct PTNode nodesMAX_TREE_SIZE; int r, n; / 根结点的位置和结点个数 PTree;二、孩子链表表示法:孩子结点结构: typedef struct CTNode int child; struct CTNode *next; *ChildPtr;双亲结点结构 typedef struct Elem data; ChildPtr
26、firstchild; / 孩子链的头指针 CTBox;树结构: typedef struct CTBox nodesMAX_TREE_SIZE; int n, r; / 结点数和根结点的位置 CTree;三、树的二叉链表(孩子-兄弟)存储表示法 template class CSNode protected CSNode *firstchild, CSNode *nextsibling; public Elem data; ; 森林和二叉树的对应关系 设森林 F = ( T1, T2, , Tn ); T1 = (root, t11, t12, , t1m); 二叉树 B =( LBT, N
27、ode(root), RBT ); 则由森林转换成二叉树的转换规则为: 若 F = ,则 B = ;否则, 由 ROOT( T1 ) 对应得到 Node(root); 由 (t11, t12, , t1m ) 对应得到 LBT; 由 (T2, T3, Tn ) 对应得到 RBT. 由二叉树转换为森林的转换规则为: 若 B = , 则 F = ;否则,由 Node(root) 对应得到 ROOT( T1 );由LBT 对应得到 ( t11, t12, ,t1m);由RBT 对应得到 (T2, T3, , Tn)由此,树的各种操作均可转化成二叉树的操作来完成换言之,和树对应的二叉树,其左、右子树的概念改变成:左是孩子,右是兄弟求树的深度的算法:int TreeDepth(CSTree T) if(!T) return 0; else h1 = TreeDepth( T-Left( );h
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1