《二叉树的周游》PPT课件.ppt_第1页
《二叉树的周游》PPT课件.ppt_第2页
《二叉树的周游》PPT课件.ppt_第3页
《二叉树的周游》PPT课件.ppt_第4页
《二叉树的周游》PPT课件.ppt_第5页
已阅读5页,还剩38页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

5-2 二叉树,5.1 二叉树的概念 5.2 二叉树的周游 5.3 二叉树的存储结构 5.4 二叉搜索树 5.5 堆与优先队列 5.6 Huffman树及其应用 5.7 二叉树知识点总结,主要内容,5.2 二叉树的周游,5.2.1 抽象数据类型 5.2.2 深度优先周游二叉树 5.2.3 广度优先周游二叉树,5.2.1 抽象数据类型,一般情况下需要二叉树的各个结点存储必要的信息,对二叉树的操作和运算也主要集中在访问二叉树的结点信息上。 例如访问某个结点的左子结点、右子结点、父结点,或者访问结点存储的数据。 从二叉树的应用角度来看,有时还需要遍历二叉树的每个结点。,5.2.1 抽象数据类型,在二叉树的抽象数据类型中,定义了二叉树基本操作的集合,在具体应用中可以以此为基础进行扩充 为了强调抽象数据类型与存储无关,在此并没有具体规定该抽象数据类型的存储方式,5.2.1 抽象数据类型,【代码5.1】 二叉树结点的抽象数据类型(ADT) template class BinaryTreeNode friend class BinaryTree / 声明二叉树类为结点类的友元类,以便访问私有数据成员 private: T info; / 二叉树结点数据域 public: BinaryTreeNode(); / 缺省构造函数 BinaryTreeNode(const T / 子树构造结点,5.2.1 抽象数据类型,T value() const; / 返回当前结点的数据 BinaryTreeNode* leftchild() const; / 返回当前结点左子树 BinaryTreeNode* rightchild() const; / 返回当前结点右子树 void setLeftchild(BinaryTreeNode* l) / 设置当前结点的左子树 void setRightchild(BinaryTreeNode* r) ; / 设置当前结点的右子树 void setValue(const T,5.2.1 抽象数据类型,【代码5.2】 二叉树的抽象数据类型 template class BinaryTree private: BinaryTreeNode* root; / 二叉树根结点 public: BinaryTree() root = NULL; / 构造函数 BinaryTree() DeleteBinaryTree(root); / 析构函数 bool isEmpty() const; / 判定二叉树是否为空树 BinaryTreeNode* Root()return root; / 返回二叉树根结点,5.2.1 抽象数据类型,BinaryTreeNode* Parent(BinaryTreeNode *current); / 返回当前结点的父结点 BinaryTreeNode* LeftSibling(BinaryTreeNode *current); / 返回当前结点的左兄弟 BinaryTreeNode* RightSibling(BinaryTreeNode *current); / 返回当前结点的右兄弟 void CreateTree(const T / 删除二叉树或其子树 ;,遍历(周游)二叉树,遍历是树结构的一种常用的、重要的运算,是树的其它运算的基础。 遍历 按一定的规律,走遍二叉树的每个结点,使每个结点被访问一次,且只被访问一次。,遍历的过程就是把非线性结构的二叉树中的结点排成一个线性序列的过程。,遍历方式 深度优先遍历 按根、左子树、右子树三个部分进行访问 广度优先遍历(逐层遍历) 从根节点开始,向下逐层访问每个节点,在每一层次上,从左到右访问每个节点。,5.2.2 深度优先周游二叉树,设t表示当前结点,L表示左子树,R表示右子树,则对这三个部分进行访问的组合共有6种, tLR tRL LtR LRt RtL RLt 若限定先左后右,则只有tLR,LtR,LRt三种,分别称为先序遍历,中序遍历,后序遍历。,基于二叉树的递归定义,这三种深度优先周游的递归定义 (1) 前序法(tLR次序,preorder traversal)。其递归定义是 访问根结点; 按前序周游左子树; 按前序周游右子树。 (2) 中序法(LtR次序,inorder traversal)。其递归定义是 按中序周游左子树; 访问根结点; 按中序周游右子树。 (3) 后序法(LRt次序,postorder traversal)。其递归定义是 按后序周游左子树; 按后序周游右子树; 访问根结点。,5.2.2 深度优先周游二叉树,5.2.2 深度优先周游二叉树,深度周游如下二叉树 前序序列是:A B D E G C F H I 中序序列是:D B G E A C H F I 后序序列是:D G E B H I F C A,图5.5 二叉树示例,5.2.2 深度优先周游二叉树,二叉树的周游算法与表达式的“前缀”和“后缀”表示法之间有着密切的联系。例如图5.6是表达式A+B(C+D)的二叉树表示。按照前序方式周游,运算符出现在前面,参与运算的对象紧跟在其后,这样就形成了前缀表达式(波兰式): + A B + C D 按照中序方式周游,得到的结果是去掉括号的中缀表达式: A + B C + D 下面是后序方式周游的结果,得到的是后缀表达式(逆波兰式): A B C D + +,图5.6 表达式树,若二叉树为空,则返回;否则,(1) 访问根结点;,(2) 先序遍历左子树;,(3) 先序遍历右子树;,A B C,先序遍历,begin,end,先序遍历顺序:,A,B,D,F,G,C,E,H,先序遍历演示,【算法5.3】 深度优先周游二叉树或其子树 template void BinaryTree:PreOrder (BinaryTreeNode *root) / 前序周游二叉树或其子树 if (root != NULL) Visit(root-value(); / 访问当前结点 PreOrder(root-leftchild(); / 前序周游左子树 PreOrder(root-rightchild(); / 前序周游右子树 ,先序遍历递归算法,递归算法转化为非递归算法,递归算法优点 形式简洁,可读性好,正确性容易得到证明,可以给程序的编制和调试带来很大的方便。 递归算法缺点 递归调用比非递归调用消耗的时间与存储空间多,运行的效率较低。,所有的递归算法都可转化成相应的非递归算法。 将递归算法改成相应的非递归算法需要一个栈来记录调用返回的路径。通过分析递归调用的执行过程,并观察栈的变化就可得到相应的非递归算法。,先序遍历的非递归实现,先序的非递归程序实现: 1、根结点进栈 2、结点出栈,被访问 3、结点的右、左儿子(非空)进栈 4、反复执行 2、3 ,至栈空为止。,先序遍历的非递归实现,B,C,D,E,L,A,先序:A、L、B、E、C、D,e.g:,A出栈访问,L出栈访问,B出栈访问,E出栈访问,C出栈访问,D出栈访问后,栈空结束,A进栈,C、L 进栈,E、B 进栈,D进栈,给出先序遍历的非递归实现的代码,先序遍历的非递归实现,template void BinaryTree:PreOrderWithoutRecursion(BinaryTreeNode *root) using std:stack; / 使用STL中的栈 stack* aStack; BinaryTreeNode *pointer = root; aStack.push(NULL); / 栈底监视哨 while (pointer) / 或者!aStack.empty() Visit(pointer-value(); / 访问当前结点 if (pointer-rightchild() != NULL) / 非空右孩子入栈 aStack.push(pointer-rightchild(); if (pointer-leftchild() != NULL) pointer = pointer-leftchild(); / 左路下降 else / 左子树访问完毕,转向访问右子树 pointer=aStack.top(); / 获得栈顶元素 aStack.pop(); / 栈顶元素退栈 ,若二叉树为空,则返回;否则,(2) 访问根结点;,(1) 中(根)序遍历左子树;,(3) 中(根)序遍历右子树;,B A C,中序遍历,begin,end,中序遍历顺序:,A,B,D,F,G,C,E,H,中序遍历演示,中序遍历递归算法,【算法5.3】 深度优先周游二叉树或其子树 template void BinaryTree: InOrder (BinaryTreeNode *root) / 中序周游二叉树或其子树 if (root != NULL) InOrder (root-leftchild(); / 中序周游左子树 Visit(root-value(); / 访问当前结点 InOrder(root-rightchild(); / 中序周游右子树 ,非递归中序周游算法的主要思想是: 每遇到一个结点就把它推入栈,然后去周游其左子树 周游完左子树后,从栈顶托出这个结点并访问之 然后按照其右链接指示的地址再去周游该结点的右子树,中序遍历的非递归实现,begin,end,中序遍历顺序:,A,B,D,F,G,C,E,H,栈实现中序遍历非递归算法,栈用来打印结点信息及访问右子树,栈实现中序遍历非递归算法,template void BinaryTree:InOrderWithoutRecursion(BinaryTreeNode *root) using std:stack; / 使用STL中的栈 stack* aStack; BinaryTreeNode *pointer = root; while (!aStack.empty() | pointer) if (pointer) aStack.push(pointer); / 当前指针入栈 pointer = pointer-leftchild(); / 左路下降 else / 左子树访问完毕,转向访问右子树 pointer = aStack.top(); / 获得栈顶元素 aStack.pop(); / 栈顶元素退栈 Visit(pointer-value(); / 访问当前结点 pointer = pointer-rightchild(); / 指针指向右孩子 ,若二叉树为空,则返回;否则,(3) 访问根结点;,(1) 后(根)序遍历左子树;,(2) 后(根)序遍历右子树;,B C A,后序遍历,begin,end,后序遍历顺序:,F,G,D,B,H,E,C,A,后序遍历演示,后序遍历递归算法,template void BinaryTree: PostOrder (BinaryTreeNode *root) / 后序周游二叉树或其子树 if (root != NULL) PostOrder(root-leftchild(); / 后序周游左子树 PostOrder (root-rightchild(); / 后序周游右子树 Visit(root-value(); / 访问当前结点 ,后序遍历的非递归实现,在非递归的后序周游算法的主要思想: 每遇到一个结点,先把它推入栈中,去周游它的左子树 周游完它的左子树后,应继续周游该结点的右子树 游完右子树之后,才从栈顶托出该结点并访问它,后序遍历的非递归实现,解决方案: 由于访问某个结点前需要知道是否已经访问该结点的右子树,因此需要给栈中的每个元素加一个标志位tag 标志位用枚举类型Tags表示,tag为Left表示已进入该结点的左子树;tag为Right表示已进入该结点的右子树,后序遍历的非递归实现,给出非递归后序遍历此二叉树时,栈的状态变化,【算法5.6】 非递归后序周游二叉树或其子树 enum TagsLeft,Right; / 定义枚举类型标志位 template class StackElement / 栈元素的定义 public: BinaryTreeNode* pointer; / 指向二叉树结点的指针 Tags tag; / 标志位 ; template void BinaryTree:PostOrderWithoutRecursion(BinaryTreeNode* root) using std:stack; / 使用STL的栈 StackElement element;,后序遍历的非递归实现,stack aStack; BinaryTreeNode* pointer; if (root = NULL) / 如果是空树则返回 return; else pointer = root; while (!aStack.empty() | pointer) /如果当前指针非空则压栈并下降到最左子结点 while (pointer != NULL) element.pointer = pointer; element.tag = Left; / 置标志位为Left, 表示进入左子树 aStack.push(element); pointer = pointer-leftchild(); element = aStack.top(); / 获得栈顶元素,后序遍历的非递归实现,后序遍历的非递归实现,aStack.pop(); / 栈顶元素退栈 pointer = element.pointer; if (element.tag = Left) / 如果从左子树回来 element.tag = Right; / 置标志位为Right, 表示进入右子树 aStack.push(element); pointer = pointer-rightchild(); else / 如果从右子树回来 Visit(pointer-value(); / 访问当前结点 pointer = NULL; / 置point指针为空,以继续弹栈 ,三种遍历算法的比较,相同点: 如果把访问根结点这个不涉及递归的语句抛开,则三个递归算法走过的路线是一样的。,三种遍历算法的比较,不同点: 前序遍历是每进入一层递 归调用时先访问根结点, 然后再依次向它的左、右 子树执行递归调用; 中序遍历是在执行完左子 树递归调用后再访问根结 点,然后向它的右子树递 归调用; 后序遍历则是在从右子树 递归调用退出后访问根结 点。,复杂度分析,对于有n个结点的二叉树,周游完树的每一个元素都需要O(n)时间 只要对每个结点的处理(函数Visit的执行)时间是一个常数,那么周游二叉树就可以在线性时间内完成 所需要的辅助空间为周游过程中栈的最大容量,即树的高度 最坏情况下具有n个结点的二叉树高度为n,则所需要的空间复杂

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论