Chapter 06 Tree and binary tree 第六章 树和二叉树

Slides:



Advertisements
Similar presentations
Course 1 演算法: 效率、分析與量級 Algorithms: Efficiency, Analysis, and Order
Advertisements

基本概論 Basic concepts.
第7章 樹與二元樹 (Trees and Binary Trees)
動畫與遊戲設計 Data structure and artificial intelligent
第6章 二叉树和树 前面的章节主要讨论的是线性结构,二叉树和树属于非线性的结构。遍历非线性结构比线性结构要麻烦。
计算机软件技术基础 数据结构与算法(4).
数据结构——树和二叉树 1/96.
引入树 线性表、堆栈和队列都是线性结构,它们的共同特点:一对一; 计算机对弈中的一个格局可能有多个后继格局,用线性结构难以描述。
第六章 树和二叉树.
第六章 树和二叉树.
第6章 树和二叉树 (Tree & Binary Tree)
第5章 树( Tree )和二叉树 5.1 树的基本概念 5.2 二叉树 5.3 遍历二叉树和线索二叉树 5.4 树和森林
CH6 树和二叉树 6.1 树的定义和基本术语 6.2 二叉树 6.3 遍历二叉树和线索二叉树 6.4 树和森林
数据结构 第六章 树与二叉树 深圳大学计算机系 蔡茂国.
第五章 树 东南大学计算机学院 方效林 本课件借鉴了清华大学殷人昆老师 和哈尔滨工业大学张岩老师的课件.
数据结构 第6章 树和二叉树 什么是树和二叉树?? 二叉树的遍历 数据结构.
Minimum Spanning Trees
§4 Additional Binary Tree Operations
第六章 树和二叉树 6.1 树的定义和基本术语 6.2 二叉树 6.3 遍历二叉树和线索二叉树 6.4 树和森林 6.6 赫夫曼树及其应用.
Chap4 Tree.
Tree(樹) 什麼是「樹」? 「樹」的範例 「樹」的定義 「樹」的表示法.
Chapter 5 Tree & Binary Tree
單向鏈結串列 Singly Linked Lists.
樹狀結構 陳怡芬 2018/11/16 北一女中資訊專題研究.
Chapter8 Binary and Other Trees
講師:郭育倫 第3章 基本資料結構 講師:郭育倫
Chapter 6 Graph Chang Chi-Chung
数 据 结 构 Ch.6 树 计 算 机 学 院 肖明军
演算法方式總覽 The Divide-and-Conquer Strategy (個各擊破)(binary Searching、Quick Sort…. ) The Greedy Method(貪婪演算法) (Prim MST、Kruskal MST、Djikstra's algorithm) Dynamic.
哈夫曼编码.
第12章 樹狀搜尋結構 (Search Trees)
第六章 二叉树和树 6.1树的基本概念 6.2二叉树 6.3二叉树遍历 6.4线索二叉树 6.5树和森林 6.6树的应用(霍夫曼树及其编码)
第十五章 Linked List, Stack and Queue
4 堆疊與佇列 4.1 前言 四種基本的資料結構 (可儲存資料的容器) 陣列 (Array)、串列(List): 最基本
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第六章 树和二叉树.
第三章 栈与队列 £3.1 栈 £3.3 队列 £3.2 栈的应用举例 £3.1.1 栈的定义 £3.1.2 栈的顺序存储结构
第3章 栈和队列(一).
第三章 栈和队列.
第 七 章 樹狀結構 課程名稱:資料結構 授課老師:________ 2019/1/1.
重點 資料結構之選定會影響演算法 選擇對的資料結構讓您上天堂 程式.
教 师:曾晓东 电 话: E_mail: 计算机软件技术基础 教 师:曾晓东 电 话: E_mail:
樹狀結構 Tree Structure chapter 7 德明科技大學資訊科技系.
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
Data Structure.
6.6 Huffman树及其应用 王 玲.
6.3 遍历二叉树和线索二叉树(知识点二) 遍历二叉树 一、问题的提出
二叉树和其他树 (Binary and other trees)
第六章 樹狀結構 T r e e 版權屬作者所有,非經作者 同意不得用於教學以外用途.
第三章 栈和队列.
严蔚敏、吴伟民编著 清华大学出版社 学习网站:
樹 2 Michael Tsai 2013/3/26.
感謝同學們在加分題建議. 我會好好研讀+反省~
Tree & Binary Tree.
第6章 树和二叉树 本章主题:树、二叉树 教学目的:掌握树和二叉树的类型定义、运算及存储结构 教学重点:树的各种表示、各种存储方式和运算,
第6章 树与二叉树 6.1 树的概念和运算 6.2 二叉树 6.3 树和森林 6.4 树的典型应用 6.5 本章小结.
二叉树的遍历.
105-1 Data Structure Exam /12/27.
第六章 树和二叉树 £6.1 树 £6.2 二叉树 £6. 3 二叉树的存储结构 £6.4 二叉树的遍历与线索化 £6.1.1 树的定义
Total Review of Data Structures
第六章 树和二叉树 学习要点 理解树的定义和基本术语,重点了解二叉树的定义、性质、存储结构; 掌握二叉树遍历的递归算法及它的典型运算;
Linked Lists Prof. Michael Tsai 2013/3/12.
第7章 樹與二元樹(Trees and Binary Trees)
資料結構使用Java 第9章 樹(Tree).
本教學投影片係屬教科書著作之延伸,亦受著作權法之保護。
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
树和二叉树(一).
计算机问题求解 – 论题 基本的数据结构 2018年05月09日.
Data Structure.
JAVA 程式設計與資料結構 第十七章 Tree.
Presentation transcript:

Chapter 06 Tree and binary tree 第六章 树和二叉树 Prof. Qing Wang

本章学习的线索 主要线索 重点 难点 树和二叉树的定义及表示 二叉树的遍历 树、森林和二叉树的转换 哈夫曼树和哈夫曼编码 二叉树的遍历及线索化 树和二叉树 的定义和 表示 二叉树的遍历(递归和非递归)及线索化 树、森林和 二叉树的转换 哈夫曼树和哈夫曼编码 Prof. Q. Wang

Contents Definition and notations of Tree and Forest Notations and representation of binary tree Binary tree traversal Threading binary tree Reconstruction of binary tree Transformation among Tree, Forest and binary tree Huffman tree and Huffman coding Prof. Q. Wang

6.1 Definition and notations of Tree 6.1.1 Definition of Tree A tree is a set of n nodes. If n=0, it is a NULL tree. If n>0, then 1. It has a so-called “root” node which only has successors and without predecessor. 2. Except the root, the remainders can be partitioned into m (m>0) un-overlapped subsets T1,T2,…, Tm, each of which is also a tree. They are called the sub-trees of the root. Note: The root of the tree has no predecessor and 0~m successors (sub-trees). Prof. Q. Wang

基本操作: InitTree(&T); DestroyTree(&T); CreateTree(&T); ClearTree(&T); TreeEmpty(T); TreeDepth(T); Root(T); Value(T, cur_e); Assign(T, cur_e, value); Parent(T, cur_e); LeftChild(T, cur_e); RightSibling(T, cur_e); InsertChild(&T, &p, i, c); DeleteChild(&T, &p, i); TravseTree(T, visit()); ADT描述参见课本 pp118 Prof. Q. Wang

Representations of tree Tree form Representations of tree A B C D E F G H I J K L M General list form (A(B(E(K,L),F), C(G), D(H(M),I,J)) Wen Graph I J F K L G M C H D B E A Retraction form A B D E I J F C G H Prof. Q. Wang

6.1.2 Notations Node (结点):包含一个数据元素及若干指向其子树的分支。 Leaf (树叶)、Branch node(分支结点) Parent node (父结点)、child node(子结点) Edge (边) :父结点x到子结点y的有序对 <x,y> Sibling (兄弟) :同一双亲的孩子 Ancestor (祖先) 、offspring (子孙): 堂兄弟:双亲在同一层的结点 Layer (结点的层数、树的层数):规定根的层数为1 Degree (结点的度、树的度):结点拥有的子树数目 Depth (树的高度或深度):树中结点的最大层次称为树的深度 Path and Path length (路径和路径长度) A B C D E F G H I J K L Prof. Q. Wang

Examples 1 2 3 4 Prof. Q. Wang

6.1.3 Forest (Orchard) A set of trees. 森林是m (m>=0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。 就逻辑结构而言,任何一棵树是一个二元组Tree=(root, F),其中root称为树的根结点;F是m (m>=0)棵子树构成的森林,F=(T1, T2,…,Tm), 其中Ti=(ri, Fi)称作根root的第i棵子树;当m0时,在树根和其子树森林之间存在下列关系: 由这个定义,可以得到森林和二叉树之间转换的递归定义。 RF={<root, ri> | i=1,2,…,m, m>0} Prof. Q. Wang

6.1.4 Basic functions for the tree 树的基本运算通常有以下几种: 1. Initialization - 创建一棵空树; 2. IsEmpty - 判断某棵树是否为空; 3. GetRoot - 求树中的根结点,若为空树,则返回一特殊值; 4. GetParent - 求树中某个指定结点的父结点,当指定结点为根时,返回一特殊值; 5. GetFirstChild - 求树中某个指定结点的最左子结点,当指定结点为树叶时,它没有子女,则返回一特殊值; 6. GetRightSibling - 求树中某个指定结点的右兄弟结点,当指定结点没有右兄弟时,返回一特殊值; 7. Traversal - 树的遍历(周游),即按某种方式访问树中的所有结点,并使每个结点恰好被访问一次。 Prof. Q. Wang

Discussion Storage design of tree General list Prof. Q. Wang

Discussion Storage design of tree Fixed multiple-branch list Number of branch:tree’s degree Null Pointer? Variable multiple-branch list Number of branch:node’s degree data child1 child2 child3 childd Prof. Q. Wang

6.2 Binary tree 6.2.1 Basic notations 二叉树的5种基本形态: ADT描述参见课本 pp121。 Prof. Q. Wang

InitBiTree(&T); DestroyBiTree(&T); CreateBiTree(&T, definition); ClearBiTree(&T); BiTreeEmpty (T); BiTreeDepth(T); Root(T); Value(T, e); Assign(T, e,value); Parent(T, e); LeftChild(T, e); RightChild(T, e); LeftSibling(T, e); RightSibling(T, e); InsertChild(T, p, LR, c); DeleteChild(T, p, LR); PreOrderTravse(T, visit()); InOrderTravse(T, visit()); PostOrderTravse(T, visit()); LevelOrderTravse(T, visit()); ADT描述参见课本 pp121 Prof. Q. Wang

Examples of binary tree Binary tree = (Root, Left sub-binary-tree, Right sub-binary-tree) Prof. Q. Wang

★ ★ 完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。 二叉树不是树的特殊情形,尽管树和二叉树的概念之间有许多类似,但它们是两个概念。树和二叉树之间最主要的差别是: Full binary tree (满二叉树):如果一棵二叉树的任何结点或者是树叶,或有两棵结构完全相同的非空子树,则此二叉树称作“满二叉树” 。 Complete binary tree (完全二叉树):如果一棵二叉树至多只有最下面的两层结点度数可以小于2(度为1的结点只能在倒数第二层),并且最下面一层的结点(叶子)都集中在该层最左边的若干位置上,则此二叉树称为“完全二叉树”。完全二叉树不一定是满二叉树。 ★ 二叉树中结点的子树要区分为左子树和右子树,即使在结点 只有一棵子树的情况下也要明确指出该子树是左子树还是右 子树。 ★ 完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。 Prof. Q. Wang

Generic binary tree 1 Generic binary tree 2 Full binary tree D E F G H I A B C D E F G H I Generic binary tree 1 Generic binary tree 2 A B C F G D E H I J A B C F G D E Full binary tree Complete binary tree Prof. Q. Wang

6.2.2 Characteristics of binary tree (1) Suppose that binary tree is leveled from 1, there are at most 2i-1 nodes at the level i (i1). 在二叉树的第i层上至多有2i-1个结点(i  1)。 可以用归纳法证明。 (2) If the height of binary tree is k (k  1), the number of nodes is at most 2k-1. 深度为k的二叉树至多有2k-1个结点(k>=1)。 Level 1 20 Level 2 21 2k-1 Level 3 22 Level k 2k-1 Prof. Q. Wang

6.2.2 Characteristics of binary tree (3) In binary tree, if the number of leaves is n0, and the number of nodes with left and right children is n2, then n0=n2+1 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。 证明: 设n1为二叉树中度为1的结点数。则有, n=n0+n1+n2 再看二叉树中的分支数。除了根结点外,其余结点都有一个分支指向它,设B为分支总数,则n=B+1。由于这些分支都是由分支为1或2的结点发出的,因此有: B=n1+2n2 于是有 n=n1+2n2+1 n1+2n2+1=n0+n1+n2 => n0=n2+1 Prof. Q. Wang

(4) The depth of the complete binary tree with n nodes is 证明:假设完全二叉树的深度为k,则根据性质2和完全二叉树的定义有 2k-1-1< n <= 2k-1 或 2k-1 <= n < 2k 于是 k-1<=log2n < k 因k是整数,所以 Prof. Q. Wang

(5) 如果对一棵有n个结点的完全二叉树按层序从1开始编号,则对任一结点i(1<=i<=n),有: a) 如果i=1, 则结点i是二叉树的根, 无双亲; 如果i>1, 则其双亲结点是 i/2 ; b) 如果2i>n, 则结点i无左孩子;否则其左孩子是结点2i; c) 如果2i+1>n, 则结点i无右孩子;否则其右孩子是结点2i+1. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 7 8 9 10 11 12 Prof. Q. Wang

Extended binary tree (扩充二叉树 ): 对一个二叉树进行“扩充”,可得到扩充的二叉树。扩充的方法是: 把原二叉树的结点都变为度数为2的分支结点,也就是说,如果原结点的度数为2,则不变,度数为1,则增加一个分支,度数为0 (树叶)增加两个分支。 在扩充的二叉树里外部结点 (叶结点)都是新增加的结点。如果原二叉树有n个结点,在扩充的二叉树里这n个结点的度数都是2。因此扩充的二叉树有2n条边,2n+1个结点,除n个原来二叉树结点,还有n+1个是新增加的外部结点,也就是说:在扩充的二叉树里,新增加的外部结点的个数比原来的内部结点个数多1。 a b c d e f 1 2 3 4 6 7 5 a b c d e f Prof. Q. Wang

“外部路径长度”E:在扩充的二叉树里从根到每个外部结点的路径长度之和。 “内部路径长度”I:在扩充的二叉树里从根到每个内部结点的路径长度之和。 Example: I=1+1+2+3+2=9 E=2+2+4+4+3+3+3=21, n=6 关系: E = I + 2n 请证明上述关系式。 a b c d e f 1 2 3 4 6 7 5 Prof. Q. Wang

6.2.3 Basic functions for binary tree 二叉树的基本运算通常有以下几种: 1. Initialization - 创建一棵空二叉树; 2. IsEmpty - 判断某棵二叉树是否为空; 3. GetRoot -求二叉树中的根结点,若为空二叉树,则返回一特殊值; 4. GetParent - 求二叉树中某个指定结点的父结点,当指定结点为根时,返回一特殊值; 5. GetLeftChild - 求二叉树中某个指定结点的左子女结点,当指定结点没有左子女时,返回一特殊值; 6. GetRightChild - 求二叉树中某个指定结点的右子女结点,当指定结点没有右子女时,返回一特殊值; 7. Traversal - 二叉树的遍历,即按某种方式访问二叉树中的所有结点,并使每个结点仅仅被访问一次。 Prof. Q. Wang

6.3 Storage of Binary Tree 6.3.1 Sequential form 用一组地址连续的存储单元依次自上而下、自左而右存储完全二叉树的结点。对于一般树,则应将其每个结点与完全二叉树上的结点相对照,存储在一维数组的相应分量中。 A B C D E F G A B C D E F G 顺序表 A B C D E F G Prof. Q. Wang

#define MAXNODE 100 /* 定义二叉树中结点的最大个数 */ //Declaration #define MAXNODE 100 /* 定义二叉树中结点的最大个数 */ typedef struct SeqBTree /* 顺序二叉树类型定义 */ { DataType nodelist[MAXNODE+1]; int n; /* 改造成完全二叉树后,结点的个数 */ }SeqBTree, *PSeqBTree; Prof. Q. Wang

6.3.2 Linked form 由二叉树的定义可知,二叉树的结点由一个数据元素和两个分支构成,因此在表示时,至少需要包含三个域:数据域和左、右指针域。如果想能够找到父结点,则可以增加一个指向父结点的指针域。利用这两种结点结构所得的二叉树存储结构分别称为二叉链表和三叉链表。 //Declaration typedef struct BinTreeNode { DataType info; struct BinTreeNode *lchild; struct BinTreeNode *rchild; }BinTreeNode, *PBinTreeNode, BinTree, *PBinTree; Prof. Q. Wang

Example Prof. Q. Wang

Static tri-branch linked list Prof. Q. Wang

Quick Review Tree and Binary tree Binary tree ADT Concepts and notations Binary tree Physical form: bi-branch linked list Complete binary tree  Sequential list 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 … 11 12 Prof. Q. Wang

6.4 Traversal of binary tree 6.4.1 Definition of Traversal Traversing Binary Tree (二叉树的遍历):按某条搜索路径访问树中每一个结点,使得每个结点被访问且仅被访问一次 (once and only once)。 Depth-first traversal (深度优先): 由于二叉树是由根结点和左右子树构成的,因此访问时就可以有6种访问顺序。如果限定访问从左到右进行,则只有三种情况,分别称为先根次序 (先序)、中根次序 (中序)、后根次序 (后序),相应的输出序列称为先序、中序和后序序列。 Level-first traversal (按层次遍历): Prof. Q. Wang

Expression: a-b/c+d Fig.1 (a-b)/(c+d) Fig.2 a-(b/c+d) Fig.3 a-b/c+d + / d a b c d b c Fig.1 (a-b)/(c+d) Fig.2 a-(b/c+d) + - - d a / a / b + b c c d Fig.3 a-b/c+d Fig.4 a-b/(c+d) Prof. Q. Wang

从上图我们可以看出,二叉树表示一个算术表达式。二叉树的每一个子树对应一个子表达式,而且二叉树的左右次序也对应了表达式的运算次序。 对图1进行先根、后根和中根次序的遍历得到如下的结点序列: 先根:/-ab+cd 后根:ab-cd+/ 中根:a-b/c+d 这些序列分别称为表达式的前缀表示法、后缀表示法 (逆波兰表示法) 和中缀表示法。逆波兰表示法可以简化表达式的计算(why?) / - a b + c d Fig.1 (a-b)/(c+d) Prof. Q. Wang

6.4.2 Traversal algorithms Recursive algorithms (递归算法) InOrderTraverse (中序遍历) PreOrderTraverse (先序遍历) PostOrderTraverse (后序遍历) Non-recursive algorithms (非递归算法) LevelOrderTraverse (按层次遍历) Prof. Q. Wang

Inorder traversal a + b * c - d - e / f Framework of Inorder traversal If binary tree T is NULL, NULL operation else 1) Inorder traverse the left sub-tree 2) Visit the root 3) Inorder traverse the right sub-tree The result of Inorder traversal is a + b * c - d - e / f Syntax tree of expression Prof. Q. Wang

Recursive Inorder Traversal Status InOrderTraverse (PBinTree T, Status (*Visit)(ElemType e)) { if (T){ InOrderTraverse (T->lchild, Visit); (*Visit)(T-> info); InOrderTraverse (T->rchild, Visit); } int PrintElement (ElemType e) printf (e); return OK; Prof. Q. Wang

a + b * c - d - e / f Recursive tree of Inorder traversal 1 2 8 3 4 10 6 5 9 7 11 a + b * c - d - e / f Prof. Q. Wang

Preorder traversal - + a * b - c d / e f Framework of Preorder traversal If binary tree T is NULL, NULL operation else 1) Visit the root 2) Preorder traverse the left sub-tree 3) Preorder traverse the right sub-tree The result of Preorder traversal is - + a * b - c d / e f Syntax tree of expression Prof. Q. Wang

Recursive Preorder Traversal Status PreOrderTraverse (PBinTree T, Status (*Visit)(ElemType e)) { if (T){ (*Visit)(T-> info); PreOrderTraverse (T->lchild, Visit); PreOrderTraverse (T->rchild, Visit); } int PrintElement (ElemType e) printf (e); return OK; Prof. Q. Wang

Postorder traversal a b c d - * + e f / - Framework of Postorder traversal If binary tree T is NULL, NULL operation else 1) Postorder traverse the left sub-tree 2) Postorder traverse the right sub-tree 3) Visit the root The result of Postorder traversal is a b c d - * + e f / - Syntax tree of expression Prof. Q. Wang

Recursive Postorder Traversal Status PostOrderTraverse (PBinTree T, Status (*Visit)(ElemType e)) { if (T){ PostOrderTraverse (T->lchild, Visit); PostOrderTraverse (T->rchild, Visit); (*Visit) (T-> info); } int PrintElement (ElemType e) printf (e); return OK; Prof. Q. Wang

Other recursive algorithm of binary tree 1. Get the number of nodes of a binary tree (求二叉树的结点个数) int count ( PBinTree T ) { if ( !T ) return 0; else return 1 + count ( T->lchild ) + count ( T->rchild ); } Prof. Q. Wang

Other recursive algorithm of binary tree 2. Get the depth of a binary tree (求二叉树的深度) int depth (PBinTree T) { if ( !T ) return 0; else return 1+Max{depth ( T->lchild ) , depth ( T->rchild ) }; } Prof. Q. Wang

Initialization of binary tree via PreOrder Input:Get the character sequentially A B C Ø Ø D E Ø G Ø Ø F Ø Ø Ø int CreateBinTree (PBinTree T ) //按先序次序构造二叉链表 { scanf(&ch); if ( ch == ‘Ø’ ) T = NULL; else { if ( !(T = (PBinTree) malloc(sizeof(BinTreeNode)))) exit(-1); T -> info = ch; CreateBinTree (T->lchild); CreateBinTree (T->rchild); } A B C D E F G A B C Ø Ø D E Ø G Ø Ø F Ø Ø Ø Prof. Q. Wang

Non-recursive traversal algorithm 二叉树遍历的非递归算法 ★ ★ 先序遍历的基本思想: 遇到一个结点,访问之,接着把压入栈中,然后去遍历它的左子树。遍历完它的左子树后,从栈顶弹出这个结点,然后再去遍历它的右子树。 ★ 中序遍历的基本思想: 遇到一个结点,就把它压入栈中,去遍历它的左子树,遍历它的左子树后,从栈顶弹出这个结点并访问之,然后再去遍历它的右子树。 对于先根遍历,可以通过适当判断减少进出栈的次数:访问一个结点之后,仅当该结点左、右子树都不空时才把结点的右子女压进栈。这样可以节省算法的时间与空间开销。 Prof. Q. Wang

ptr //Preorder Traversal struct StackNode { PBinTree ptr; }; void PreOrderTraverse (PBinTree T ) { Stack S; PBinTree p; StackEmpty( S ); p = T; do { while ( p ) { printf ( p->info); Push( S, p ); p = p->lchild; } if ( !IsEmpty( S ) ) { p = getTop( S ); Pop( S ); p = p->rchild; } while ( p || !IsEmpty( S ) ); struct StackNode { PBinTree ptr; }; ptr Prof. Q. Wang

先序遍历中栈的变化 Stack state in PreOrderTraversal - - + - + a - + - - * - * b Prof. Q. Wang

先序遍历中栈的变化 Stack state in PreOrderTraversal - - d - / / e / f 栈空 Prof. Q. Wang

ptr //Inorder Traversal void InOrderTraverse (PBinTree T ) { Stack S; PBinTree p; StackEmpty( S ); p = T; do { while ( p ) { Push( S, p ); p = p->lchild; } if ( !IsEmpty( S ) ) { p = getTop( S ); Pop( S ); printf ( p->info); p = p->rchild; } while ( p || !IsEmpty( S ) ); ptr Prof. Q. Wang

- - + - + a - + - - * - * b - * - - - c - Stack state in InOrderTraversal 中序遍历中栈的变化 - - + - + a - + - - * - * b - * - - - c - Prof. Q. Wang

Stack state in InOrderTraversal 中序遍历中栈的变化 - - d - / / e / f 栈空 Prof. Q. Wang

需要考虑二次进栈!!! 后序遍历的基本思想是: 后序非递归算法比较复杂,每个结点要到它们左、右子树都遍历完时才得以访问,所以在去遍历它的左、右子树之前都需要进栈。 当它出栈时,需要判断是从左子树回来(即刚遍历完左子树,此时需要去遍历右子树),还是从右子树回来(即刚遍历完右子树,此时需要去访问这个结点)。因此,进栈的结点需要伴随着一个标记tag 。 L 表明遍历它的左子树 tag = R 表明遍历它的右子树 需要考虑二次进栈!!! Prof. Q. Wang

typedef struct StackNode { enum tag { L, R }; PBinTree ptr; } StackNode; ptr tag (LR) 后序遍历根为 T 的二叉树, 存储结构为二叉链表, S 是存储所经过二叉树结点的工作栈。其中, tag 是结点标记。当 tag=L 时, 表示刚才是在左子树中, 从左子树退出时, 还要去遍历右子树。当 tag =R 时, 表示刚才是在右子树中, 在从右子树中退出时, 去访问位于栈顶的结点。 Prof. Q. Wang

//Postorder Traversal void PostOrderTraverse (PBinTree T ) { Stack S; PBinTree p; StackNode w; StackEmpty( S ); p = T; do { while ( p ) { w.ptr = p; w.tag = L; Push( S, w); p = p->lchild; } - + a L - + a L R - + L - + L R Prof. Q. Wang

while ( continue && ! IsEmpty( S ) ) { w = getTop( S ); Pop( S ); int continue = 1; while ( continue && ! IsEmpty( S ) ) { w = getTop( S ); Pop( S ); p = w.ptr; switch (w.tag) { case L : w.tag = R; Push( S, w); continue = 0; p = p->rchild; break; case R : printf (p->info); } } while ( p || !IsEmpty( S ) ); 二次进栈 Prof. Q. Wang

Stack state in InOrderTraversal 后序遍历中栈的变化 - L - + L - + a L - + a L R - + a L R - + L - + L R - + L R * b - + L R * b - + L R * b - + L R * - + L R * c - + L R * c - + L R * c - + L R * d - + L R * - + L R * - + L R * Prof. Q. Wang

Stack state in InOrderTraversal 后序遍历中栈的变化 - + L R * d - + L R * d - + L R * - + L R * - + L R - R - R / L - R / L e - R / L e - R / L e - R / L - R / - R / f L - R / f - R / f - R / - R 栈空 Prof. Q. Wang

Quick Review Structure of binary tree Traversal of binary tree Binary linked list Traversal of binary tree Recursive algorithms Non-recursive algorithms Prof. Q. Wang

LevelOrderTraversal From the root, visit the node from the top to the bottom, and from the left to the right at the same level. Queue involved Prof. Q. Wang

//Level order Traversal void LevelOrderTraverse (PBinTree T ) { Queue qu; PBinTree p; if ( !T ) { printf( “End of level order traversal!\n”); return; } EnQueue ( qu, T ); while ( !empty( qu ) ) { DeQueue ( qu, p ); printf ( p->info ); //DeQueue & output if ( p->lchild != NULL ) //Left child EnQueue ( qu, p->lchild ); //EnQueue if ( p->rchild != NULL ) //Right child EnQueue ( qu, p->rchild ); //EnQueue struct QueueNode { PBinTree ptr; }; Prof. Q. Wang

Analysis of binary tree traversal Time complexity (时间复杂度) O(n) Space complexity (空间复杂度) Depth-first methods (深度优先的方法) O(栈的深度) Level-first method (按层次遍历) n: number of binary tree nodes (二叉树的结点个数) Prof. Q. Wang

void Postorder(PBinTree t){ Stack s; PBinTreeNode p; /* 周游时当前要处理结点的位置 */ char flag; /* 结点p的标志量 */ char continueflag; /* 表明是否要继续退栈,当从右子树返回 时,则要在访问完根之后继续退栈 */ ElemType elem; if (t == NULL) return; InitStack(&s); p = t; /* 从根结点开始 */ do{ /* 每执行一次大循环进入一棵由p指出根的子树去周游 */ while (p != NULL) /* 反复地把遇到的结点进栈并进入 它的左子树 */ { elem.ptr = p; elem.tag = '1'; Push(&s, elem); p = p->llink; } continueflag = '1'; Prof. Q. Wang

while ( continueflag == '1' && !IsStackEmpty(s)) { Pop(&s, &elem); p = elem.ptr; if (elem.tag == ‘1’) /* 如果是从左子树回来,则 改标志重新进栈,停止退栈并进入右子树 */ elem.tag = 'r'; Push(&s, elem); continueflag = '0'; p = p->rlink; } else visit(p->info); } while (!IsStackEmpty(s)); /* 栈为空时,全部周游完 */ DestroyStack(&s); Prof. Q. Wang

6.4.3 Reconstruction of Binary Tree ★ ★ Problem 1 Given a binary tree, is Preorder sequence, or Inorder sequence, or Postorder sequence Unique? Problem 2 Given a preorder sequence of BT, can you reconstruct an unique binary tree? How about Inorder sequence, postorder sequence? Prof. Q. Wang

Problem 3 Given preorder and inorder sequences of BT, can you reconstruct this binary tree? Preorder: abcd; Inorder: badc; Given postorder and inorder sequences of BT, can you reconstruct this binary tree? Postorder: bdca; Inorder: badc; Given preorder and postorder sequences of BT, can you reconstruct this binary tree? Preorder: abcd; Postorder: dcba; Prof. Q. Wang

Example ABECDFGHIJ EBCDAFHIGJ Suppose that the preorder sequence of BT is ABECDFGHIJ and the inorder sequence of BT is EBCDAFHIGJ try to reconstruct this binary tree. Prof. Q. Wang

Preorder sequence: ABECDFGHIJ Inorder sequence: EBCDAFHIGJ Prof. Q. Wang

Preorder sequence: ABECDFGHIJ Inorder sequence: EBCDAFHIGJ Prof. Q. Wang

6.4.4 Counting of BT ★ ★ 123456789 Problem (问题) 给你一个二叉树的先序遍历序列,问具有这样先序序列的二叉树有多少棵? 123456789 Prof. Q. Wang

Example (1) 123 132 213 231 321 For example, { 1, 2, 3 } Preorder sequence 123 Inorder traversed sequences are 123, 132, 213, 231, and 321. 123 132 213 231 321 Prof. Q. Wang

Example (2) n=0, 1, 2, 3, 4 Prof. Q. Wang

Conclusion bi bn-i-1 Prof. Q. Wang

定义生成函数 由于 Prof. Q. Wang

Prof. Q. Wang

Questions Prof. Q. Wang

Questions Prof. Q. Wang

6.4 Traversal of binary tree 6.4.1 Definition of Traversal Traversing Binary Tree (二叉树的遍历):按某条搜索路径访问树中每一个结点,使得每个结点被访问且仅被访问一次 (once and only once)。 Depth-first traversal (深度优先): 由于二叉树是由根结点和左右子树构成的,因此访问时就可以有6种访问顺序。如果限定访问从左到右进行,则只有三种情况,分别称为先根次序 (先序)、中根次序 (中序)、后根次序 (后序),相应的输出序列称为先序、中序和后序序列。 Level-first traversal (按层次遍历): Prof. Q. Wang

Preorder sequence: ABECDFGHIJ Inorder sequence: EBCDAFHIGJ Prof. Q. Wang

6.5 Threading binary tree 遍历二叉树是按一定的规则将二叉树中结点排列成一个线性序列,这实际上是把一个非线性结构进行线性化的操作。 以二叉链表作为存储结构时,对于某个结点只能找到其左右孩子,而不能直接得到结点在任一序列中的前驱或后继。要想得到,只能通过遍历的动态过程才行。 怎样保存遍历过程中得到的信息呢? Prof. Q. Wang

(1) 增加两个指针,分别指示其前驱和后继结点 predecessor successor pre suc rchild lchild info Prof. Q. Wang

struct ThrTreeNode *lchild; struct ThrTreeNode *rchild; /* 线索树中每个结点的结构 */ { DataType info; struct ThrTreeNode *lchild; struct ThrTreeNode *rchild; struct ThrTreeNode *pre; struct ThrTreeNode *suc; }ThrTree, *PThrTree, *PThrTreeNode; root suc B D A E C pre Prof. Q. Wang

(2) 利用结构中的空链域,并设立标志,即采用如下的形式 rtag rchild lchild info ltag Prof. Q. Wang

struct ThrTreeNode /* 线索树中每个结点的结构 */ { DataType info; struct ThrTreeNode *lchild, *rchild; int ltag, rtag; }ThrTree, *PThrTree, *PThrTreeNode; 其中当ltag或rtag为0时,llink或rlink为指针;否则为1时,llink或rlink表示线索。 Prof. Q. Wang

线索链表:以上面结构构成的二叉链表作为二叉树的存储结构,叫做线索链表。 线索:指向结点前驱或后继的指针叫做线索。 线索二叉树:加上线索的二叉树叫线索二叉树。 线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。 Prof. Q. Wang

Question (1) In an InOrderThreading binary tree, where is The predecessor of a given node The successor of a given node Prof. Q. Wang

1) 若右标志是1,则右链为线索,指示其后继; 在中序线索二叉树中找结点的后继 在中序线索树中找结点后继的规律是: 1) 若右标志是1,则右链为线索,指示其后继; 2) 否则,遍历该结点的右子树时访问的第一个结点(右子树中最左下的结点)为其后继。 - NULL + + NULL a * e f b - c d Prof. Q. Wang

1) 若左标志是1,则左链为线索,指示其前驱; 在中序线索二叉树中找结点的前驱 在中序线索树中找结点前驱的规律是: 1) 若左标志是1,则左链为线索,指示其前驱; 2) 否则,遍历该结点的左子树时最后访问的结点(左子树中最右下的结点)为其前驱。 - NULL + + NULL a * e f b - c d Prof. Q. Wang

√ √ Other questions (2) In PreOrderThreading tree, The successor of the given node The predecessor of the given node In PostOrderThreading tree √ √ Prof. Q. Wang

(1) 若结点x是二叉树的根,则其前驱为空; (2) 若结点x是其双亲的左孩子或是右孩子且其双亲没有左子树,则其前驱即为双亲结点。 可分三种情况: (1) 若结点x是二叉树的根,则其前驱为空; (2) 若结点x是其双亲的左孩子或是右孩子且其双亲没有左子树,则其前驱即为双亲结点。 (3) 若结点x是其双亲的右孩子,且其双亲有左子树,则其前驱为双亲的左子树上按先序遍历出的最后一个结点。 H D G A C F B E 前驱线索 由此可见,在先序线索化树上找前驱时也需知道结点的双亲,因此需要使用三叉链表。 Prof. Q. Wang

(1) 若结点x是二叉树的根,则其后继为空; (2) 若结点x是其双亲的右孩子或是左孩子且其双亲没有右子树,则其后继即为双亲结点。 后继线索 可分三种情况: (1) 若结点x是二叉树的根,则其后继为空; (2) 若结点x是其双亲的右孩子或是左孩子且其双亲没有右子树,则其后继即为双亲结点。 (3) 若结点x是其双亲的左孩子,且其双亲有右子树,则其后继为双亲的右子树上按后序遍历出的第一个结点。 H D G A C F B E 由此可见,在后序线索化树上找后继时需知道结点的双亲,因此需要使用三叉链表。 Prof. Q. Wang

- + a * b c d e f 如何中序遍历中序线索二叉树? 可见,在中序线索二叉树上遍历二叉树,虽然时间复杂度也是O(n),但常数因子小,且不需要设栈,因此若在某程序中需要经常遍历或查找结点在遍历所得线性序列中的前驱和后继,则应采用线索链表作存储结构。 - + a * b c d e f NULL 如何中序遍历中序线索二叉树? Prof. Q. Wang

中序遍历中序线索二叉树 void InOrderTravse_Thr (PThrTree t) { PThrTreeNode p; if ( !t ) return ; p = *t; while (p->lchild!=NULL && p->ltag==0) /* 顺左子树一直向下 */ p = p->lchild; while ( !p ) { visit(p); /* 访问该结点 ,printf(p->info);*/ if ( p->rchild && p->rtag==0) { /* 右子树不是线索时 */ p = p->rchild; while ( p->lchild && p->ltag==0) p = p->lchild; /* 顺右子树根结点的左子树一直向下 */ } else p = p->rchild; /* 顺线索向下 */ Prof. Q. Wang

二叉树的中序线索化 原理 递归算法 非递归算法 在遍历的过程中,修改二叉树中原有的n+1个空指针 必备的辅助存储,设置一个指针*pre始终指向刚刚访问过的结点 递归算法 非递归算法 Prof. Q. Wang

Status InOrderTraverse (PBinTree T, Status (*Visit)(ElemType e)) { if (T){ InOrderTraverse (T->lchild, Visit); (*Visit)(T-> info); InOrderTraverse (T->rchild, Visit); } 中序遍历 void InThreading (PThrTree p, PThrTree *pre) { if (p) { InThreading (p->lchild, pre); if (!p->lchild) { p->ltag = 1; p->lchild = *pre;} if (!(*pre)->rchild) {(*pre)->rtag = 1; (*pre)->rchild = p;} *pre = p; InThreading (p->rchild, pre); } 中序线索化 Prof. Q. Wang

- + a * b c d e f *pre=null BT p - + / a * e f b - c d Prof. Q. Wang

- + a * b c d e f *pre=null BT - p + / a * e f b - c d Prof. Q. Wang

- + a * b c d e f *pre=null BT - + / p a * e f b - c d Prof. Q. Wang

- + a * b c d e f BT - + / p 1 a * e f b - *pre=null c d Prof. Q. Wang

- + a * b c d e f BT - p + / *pre 1 a 1 * e f b - c d Prof. Q. Wang

- + a * b c d e f BT *pre - + p / 1 a 1 * e f p b - c d Prof. Q. Wang

- + a * b c d e f BT *pre - + / 1 a 1 * e f p 1 b - c d Prof. Q. Wang

- + a * b c d e f p *pre BT - + / 1 a 1 * e f 1 b 1 - c d - + p / 1 a 1 * e f *pre 1 b 1 - c d Prof. Q. Wang

- + a * b c d e f *pre p BT - + a * e f b - c d / 1 1 1 1 1 - *pre + / 1 a 1 * e f 1 b 1 - p 1 c d Prof. Q. Wang

- + a * b c d e f NULL thrt 1 BT - + / 1 a 1 * 1 e 1 1 f 1 1 b 1 - 1 c 1 BT - + / 1 a 1 * 1 e 1 1 f 1 1 b 1 - 1 c 1 1 d 1 Prof. Q. Wang

递归算法 void InThreading (PThrTree p, PThrTree *pre) { if (p) { InThreading (p->lchild, pre); if (!p->lchild) { /* If left child is NULL, let it point to its previous node */ p->ltag = Thread; p->lchild = *pre; } if (!(*pre)->rchild) { /* 如果前驱的右指针空,则让它指向当前结点 */ (*pre)->rtag = Thread; (*pre)->rchild = p; *pre = p; InThreading (p->rchild, pre); } /* End of InThreading() */ 递归算法 Setup Threading Prof. Q. Wang

InOrderThreading (二叉树的中序线索化递归算法) enum tag {Link, Thread}; Status InOrderThreading (PThrTree *Thrt, PThrTree BT) { /* Threading the Binary Tree, let Thrt point to the head node */ PThrTree pre; /* point to the previous node */ /* 创建头结点 */ *Thrt = (PThrTree *) malloc (sizeof(PThrTree )); (*Thrt)->ltag = Link; (*Thrt)->rtag = Thread; (*Thrt)->rchild = *Thrt; /* 右指针指向头结点 */ if (!BT) (*Thrt)->lchild = *Thrt; /* 树空,则让其左指针指向头结点*/ Prof. Q. Wang

(*Thrt)->lchild = BT; pre = *Thrt; InThreading (BT, &pre); else { (*Thrt)->lchild = BT; pre = *Thrt; InThreading (BT, &pre); /* 线索化后,pre指向二叉树的最后一个结点*/ pre->rchild = *Thrt; /* 最后一个结点的右指针指向头结点。 */ pre->rtag = Thread; (*Thrt)->rchild = pre; /* 头结点的右指针指向最后一个结点*/ } return OK; } /* End of InOrderThreading() */ Prof. Q. Wang

InOrderThreading (二叉树的中序线索化非递归算法) void thread (PThrTree t) { Stack S; /*栈元素的类型为PThrTreeNode*/ PThrTreeNode p; /*指向当前正在访问的结点*/ PThrTreeNode pre; /*指向p的中序前驱结点*/ if ( !t ) return ; StackEmpty( S ); p = *t; pre = NULL; do { while ( p ) /* 遇到结点入栈,然后处理其左子树 */ Push( S, p ); p = p->lchild; } Prof. Q. Wang

p = getTop( S ); Pop( S ); while ( !p && !IsEmpty( S ) ) /* 左子树处理完, { 从栈顶退出结点并访问 */ p = getTop( S ); Pop( S ); if ( !pre) { if ( pre->rchild == NULL) /* 检查前驱结点的右指针 */ { pre->rchild = p; pre->rtag = 1; } if ( !p->lchild) /* 检查该结点的左指针 */ { p->lchild = pre; p->ltag = 1; pre = p; p = p->rchild; /* 处理右子树 */ } while ( p || !IsEmpty( S ) ); Setup Threading Prof. Q. Wang

★ ★ ★ 非形式地说,中序线索二叉树里的线索总是指向二叉树中更高层的结点,也就是说是“向上”指的 (如下图) 。 利用这个“向上”指的线索我们还可以在中序线索二叉树里找出指定结点在先根下的后继结点和后根下的前驱结点。 thrt ★ A ★ ★ B E 1 C 1 D 1 F G 1 H 1 K Prof. Q. Wang

1) 当一个结点有左子树时,其先根的后继为它的左子树的根结点; 2) 当一个结点没有左子树但有右子树时,其先根的后继为它的右子树的根结点; 对于先根的后继: 1) 当一个结点有左子树时,其先根的后继为它的左子树的根结点; 2) 当一个结点没有左子树但有右子树时,其先根的后继为它的右子树的根结点; 3) 当一个结点为叶子结点时,先根顺序的后继恰好为其中序下右线索所指结点的右子树的根结点。 A B E 1 C D F G H K thrt Prof. Q. Wang

1) 当一个结点有右子树时,其后根的前驱为它的右子树的根结点; 2) 当一个结点没有右子树但有左子树时,其后根的前驱为它的左子树的根结点; 对于后根的前驱: 1) 当一个结点有右子树时,其后根的前驱为它的右子树的根结点; 2) 当一个结点没有右子树但有左子树时,其后根的前驱为它的左子树的根结点; 3) 当一个结点为叶子结点时,后根顺序的前驱恰好为其中序下左线索所指结点的左子树的根结点。 A B E 1 C D F G H K thrt Prof. Q. Wang

Question 在中序线索二叉树中,如何找先序下的前驱和后序下的后继结点? 在先序、中序和后序线索二叉树中插入一个结点,或者删除一个结点,二叉树中的线索将如何维护? 主要研究中序线索二叉树。 Prof. Q. Wang

6.6 Representation of tree and forest 6.6.1 Storage of Tree (1) Parent method (双亲表示法) 用一组连续空间存储树的结点,并附设一个指示器指示其双亲结点的位置。结构类型如下: typedef struct ParTreeNode /* 树中结点结构 */ { DataType info; /* 结点中的元素 */ int parent; /* 结点的父结点位置 */ }ParTreeNode; typedef struct ParTree ParTreeNode nodelist[MAXNUM]; int n; }ParTree, *PParTree; Prof. Q. Wang

改进的方法是按一种遍历次序在数组中存放结点。其中较常见的一种方法是依次存放树的先根序列,如图: 在这种表示中,容易找到父结点及其所有的祖先;也能找到结点的子女和兄弟(虽然比较麻烦)。但它没有表示出结点之间的左右次序,所以无法求树中某个指定结点的最左子结点和右兄弟结点等基本运算。 改进的方法是按一种遍历次序在数组中存放结点。其中较常见的一种方法是依次存放树的先根序列,如图: a b c d e h i j f g info parent a -1 b e d 1 h 3 i j c f 7 g 2 4 5 6 8 9 Prof. Q. Wang

(2) Sub-list method (子表表示法) 结点表中的每一元素又包含一个子表,存放该结点的所有子结点。结点表顺序存放,子表用链接表示,子表中的元素按从左到右的次序存放。结构类型如下: typedef struct EdgeNode /* 子表中结点的结构 */ { int node_position; struct EdgeNode *link; }EdgeNode; typedef struct ChiTreeNode /* 结点表中结点的结构 */ DataType info; struct EdgeNode *children; }ChiTreeNode; Prof. Q. Wang

typedef struct ChiTree /* 树结构 */ { ChiTreeNode node_list[MAXNUM]; int root; /* 根结点的位置 */ int n; /* 结点的个数 */ }ChiTree, *PChiTree; info children a b c d e h i j f g a 1 7 1 b 2 3 2 d 3 e 4 5 6 4 h 5 i 6 j 7 c 8 9 8 f 9 g Prof. Q. Wang

(3) Left-child right-sibling method (左孩子右兄弟表示法) typedef struct CSNode { DataType info; struct CSNode *lchild; struct CSNode *rsibling; }CSTree, *PCSTree; t a b c d e h i j f g a b c d e f g h i j Prof. Q. Wang

(1) Parent method (双亲表示法) 6.2.2 Storage of Forest (1) Parent method (双亲表示法) info parent A -1 B K C 2 D E 4 H 5 F J 7 G 1 3 6 8 9 A B C H D E F G K J Prof. Q. Wang

(2) Sub-list form (子表表示法) info children A B K C D E H F J G 1 2 3 4 5 6 7 8 9 A B C H D E F G K J Prof. Q. Wang

(3) Left-child right-sibling method (左孩子右兄弟表示法) A D B C E F G K H J A B C H D E F G K J Prof. Q. Wang

6.7 Traversal of Tree and Forest 1 6.7.1 Traversal of Tree 2 3 4 1. Definition of traversal (遍历的定义) 2. Traversal methods (遍历的方法) (1)深度方向(以右图为例) a. 先根次序 (1, 2, 3, 5, 8, 9, 6, 10, 4, 7) b. 后根次序 (2, 8, 9, 5, 10, 6, 3, 7, 4, 1) (2)广度方向 层次序列 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 5 6 7 8 9 10 Prof. Q. Wang

6.7.2 Traversal of forest (森林的遍历) 1. PreOrder 先序 (A, B, C, K, D, E, H, F, J, G) 2. InOrder 中序 (B, K, C, A, H, E, J, F, G, D) A B C H D E F G K J Prof. Q. Wang

6.8 Conversion among Tree, Forest and Binary tree 6.8.1 Tree, Forest  Binary tree 由于树和二叉树都可以用二叉链表作存储结构,则以二 叉链表作媒介可以导出树与二叉树之间的一个对应关系。 A A B A B C E C B D C D E D E Prof. Q. Wang

如果F={T1, T2, …, Tm}是森林,则可按如下规则转换成一棵二叉树B={root, LB, RB}。 从二叉链表表示的定义可知,任何一棵和树对应的二叉树,其右子树必空。这样,若把森林中第二棵树的根结点看成是第一棵树的兄弟,则可以导出森林和二叉树之间的对应关系。 1. 森林转换成二叉树 如果F={T1, T2, …, Tm}是森林,则可按如下规则转换成一棵二叉树B={root, LB, RB}。 (1) 若F为空,则B为空树; (2) 若F非空,则B的根root为森林中第一棵树的根Root(T1),B的左子树LB是从T1中根结点的子树森林F1={T11, T12 ,…, T1m1}转换而成的二叉树;其右子树RB是从森林F’={T2, T3, … , Tm}转换而成的二叉树。 Prof. Q. Wang

如果B={root, LB, RB} 是一棵二叉树,则可按如下规则转换成森林F={T1, T2, …, Tm} 2. 二叉树转换成森林 如果B={root, LB, RB} 是一棵二叉树,则可按如下规则转换成森林F={T1, T2, …, Tm} (1) 若B为空,则F为空树; (2) 若B非空,则为森林中第一棵树的根Root(T1)即为 B的根root, T1中根结点的子树森林F1={T11, T12,…, T1m1} 是由B的左子树LB转换而来的森林;F中除T1之外的其余树组成的森林F’={T2, T3, … , Tm}是由B的右子树转换而成的。 根据上述递归定义很容易写出相互转换的递归算法。森林和树的操作也可转换成二叉树的操作来实现。 把树和森林转换成对应的二叉树的直观方法:凡是兄弟就用线连起来,然后去掉父母到子女的连线,只留下父母到其第一个子女的连线。 Prof. Q. Wang

Tree  Binary tree A B C E F D A B C E F D Prof. Q. Wang

Forest  Binary tree A B C H D E F G K J A B D A D C E B E F G C K H F Prof. Q. Wang

6.9 Huffman tree and coding Background if (a<60) b=‘E’; else if (a<70) b=‘D’; else if (a<80) b=‘C’; else if (a<90) b=‘B’; else b=‘A’; a<60 31500次比较 Y N E a<70 N Y D a<80 Y N C a<90 Y N B A Prof. Q. Wang

a<80 Y N 70≤a<80 a<70 a<90 Y N Y N Y N C 80≤a<90 B A Y N Y N B 60≤a<70 E D N Y 22000次比较 a<60 D Y N Prof. Q. Wang E A

1. Definition of Huffman tree (定义及构造方法) 在6.2中我们介绍了扩充二叉树及其外部路径长度的概念。用E表示某扩充二叉树的外部路径长度。则有: 其中 li 为从根到外部结点的路径长度,m为外部结点的个数。 如果给每个外部结点ki赋一个实数wi作为“权”,则将 记作“带权的外部路径长度”。 现要构造一棵以wi (i = 1, 2, …, m)为权的m个外部结点的扩充的二叉树,使得带权的外部路径长度最小。满足这一要求的扩充二叉树也称为“哈夫曼树”,也叫最优二叉树。 Prof. Q. Wang

Path length of binary tree PL=0+1+1+2+2+2+2+3 =13 PL=0+1+1+2+2+2+3+3 =14 Prof. Q. Wang

In general Minimum Prof. Q. Wang

Weighted path length of binary tree WPL=(2+4+5+7)*2=36 WPL=2*1+4*2+(5+7)*3=46 WPL=7*1+5*2+(2+4)*3=35 Prof. Q. Wang

WPL(T)= 72+52+23+43+92 =60 WPL(T)= 74+94+53+42+21 =89 2 4 7 Prof. Q. Wang

Huffman algorithm (1) 根据给定的n个权值{w1, w2, …, wn},构成n棵二叉树的集合F={T1,T2,…,Tn},其中每一棵二叉树Ti中只有一个带权为wi的根结点,其左右子树为空。 (2) 在F中选取两棵权值最小的树作为左右子树构造一棵新的二叉树,且新二叉树的根结点的权值为其左右子树是根结点权值之和。 (3) 在F中删除这两棵树,同时将新得到的二叉树加入F中。 (4) 重复(2)和(3),直到F中只含一棵树为止。该树即为哈夫曼树。 Prof. Q. Wang

Prof. Q. Wang

Structural design of Huffman tree #define MAXNODE 100 #define MAXNUM 50 struct HtNode /* 哈夫曼树结点的结构 */ { int weight; int parent, lchild, rchild; }; struct HtTree struct HtNode ht[MAXNODE]; int root; /* 哈夫曼树根在数组中的位置 */ }HtTree, *PHtTree; Prof. Q. Wang

Example A communication system has eight symbols c1, c2, c3, c4, c5, c6, c7, c8 and the probability of each symbol is 5, 25, 3, 6, 10, 11, 36, 4 Try to design a coding method. Step1: setup a Huffman tree; Step2: coding. Prof. Q. Wang

Step 0: 5 25 3 6 10 11 36 4 Step 1: 5 25 3 6 10 11 36 4 7 5 25 6 10 11 36 3 4 Prof. Q. Wang

Step 2: 7 5 25 6 10 11 36 3 4 7 11 25 10 11 36 3 4 5 6 Prof. Q. Wang

Step 3: 7 11 25 10 11 36 3 4 5 6 17 7 11 10 25 11 36 3 4 5 6 Prof. Q. Wang

22 Step 4: 17 7 11 10 11 25 36 3 4 5 6 Step 5: 39 22 17 25 36 7 11 10 11 3 4 5 6 Prof. Q. Wang

Step 6: 39 61 22 17 25 36 7 11 10 11 3 4 5 6 Prof. Q. Wang

100 Step 7: 39 61 22 17 25 36 7 11 10 11 3 4 5 6 Prof. Q. Wang

Principle and Procedure of Huffman tree n leaves (external nodes) and (n-1) internal nodes, and The last one is the root. Prof. Q. Wang

Principle and Procedure of Huffman tree Prof. Q. Wang

Principle and Procedure of Huffman tree Prof. Q. Wang

Principle and Procedure of Huffman tree Prof. Q. Wang

Root node Prof. Q. Wang

Programming of Huffman tree PHtTree huffman (int n, int *w) /* 构造具有n个叶结点的哈夫曼树*/ /* 数组w[1…n]中存放n个权值 */ { PHtTree pht; int i, j, x1, x2, m1, m2; pht = (PHtTree) malloc (sizeof (struct HtTree)); /* 创建空哈夫曼树 */ assert(pht); for( i=1; i<=2*n - 1; i++ ) { /* 置初态 */ pht->ht[i].lchild = 0; pht->ht[i].rchild = 0; pht->ht[i].parent = 0; if (i<=n) pht->ht[i].weight = w[i]; else pht->ht[i].weight = 0; } Prof. Q. Wang

pht->ht[x1].parent = n + i; /* 构造一个内部结点 */ /* 每循环一次构造一个内部结点 */ for( i=1; i < n ; i++ ) { Select (pht, n+i, &x1, &x2); pht->ht[x1].parent = n + i; /* 构造一个内部结点 */ pht->ht[x2].parent = n + i; pht->ht[n+i].weight = pht->ht[x1].weight + pht->ht[x2].weight; pht->ht[n+i].lchild = x1; pht->ht[n+i].rchild = x2; pht->root = n+i; } return pht; Prof. Q. Wang

void Select (PHtTree pht, int pos, int *x1, int *x2) { int m1 = MAXINT, m2 = MAXINT; /* 相关变量赋初值 */ for (j=1; j<pos; j++) {/* 找两个最小权的无父结点的结点 */ if (pht->ht[j].weight<m1 && pht->ht[j].parent == 0) { m2 = m1; x2 = x1; m1 = pht->ht[j].weight; x1 = j; } else if (pht->ht[j].weight<m2 && pht->ht[j].parent == 0) { m2 = pht->ht[j].weight; x2 = j; Prof. Q. Wang

补充材料 信号编码 信源编码(香农理论Shannon)和信道编码 Prof. Q. Wang

补充材料 编码(coding): 需建立码本来表达数据 码本(codebook): 用来表达一定量的信息或一组事件所需的一系列符号(如字母、数字等) 码字(code): 对每个信息或事件所赋的码符号序列 码字的长度(字长)(code length): 每个码字里的符号个数 Prof. Q. Wang

举例 解码端收到01 bit stream a c f e d f h d h g 符号 a b c d e f g h 解码端收到01 bit stream 平均码长=(0.19+0.25+0.21)*2+0.16*3+0.08*4+0.06*5+0.05*6=2.7 a c f e d f h d h g 000010101100011101111011111110 f b b c d a b a c a a a c 定长编码(自然码) 10个符号 变长编码(哈夫曼码) 13个符号 Prof. Q. Wang

2. Huffman coding (哈夫曼编码) 在一个字符集中,任何一个字符的编码都不是另一个字符编码的前缀,这种编码称为前缀编码。 我们可以利用二叉树来设计二进制的前缀编码。约定左分支表示字符‘0’,右分支表示字符‘1’,则可以用从根结点到叶子结点的路径上的分支字符串作为该叶子结点字符的编码。如此得到的编码必是前缀编码。 证明: 假设某一个叶子x结点的编码是另一个叶子结点y编码的前缀,说明从根结点到叶子结点y中间需经过结点x,从而说明x有左或右子树,这与x是叶子结点矛盾。 那么现在求最短的二进制编码实际上就是构造哈夫曼树的过程,由此得到的二进制编码,称为哈夫曼编码。 Prof. Q. Wang

由于哈夫曼树中没有度为1的结点(这类树又称严格的(strict) (或正则的)二叉树), 为什么?; 则一棵有n个叶子结点的哈夫曼树共有2n-1个结点(因n2=n0-1),可以存储在一个大小为2n-1的一维数组中。 如何选定结构类型? (1) 编码需要从叶子到根(自底向上) (2) 译码需要从根到叶子(自顶向下) Prof. Q. Wang

Huffman Tree Huffman Coding Huffman Code C1 1 C2 1 C3 C4 1 1 C5 1 C6 1 1 C2 1 C3 C4 1 1 C5 1 C6 1 1 C7 1 1 C8 1 Prof. Q. Wang

011 10 11 0000 0001 001 0100 0101 Prof. Q. Wang

Total length of these symbols is Huffman coding: c1 c2 c3 c4 c5 c6 c7 c8 0100 10 0000 0101 001 011 11 0001 5, 25, 3, 6, 10, 11, 36, 4 Total length of these symbols is 4 * 5 + 2 * 25 + 4 * 3 + 4 * 6 + 3 * 10 + 3 * 11 + 2 * 36 + 4 * 4 = 257 Prof. Q. Wang

void TraverseHuffman (PhtTree T, char** HC, int n) { static int codeLen = 0; /* 为什么使用静态变量? */ static char cd[MaxLen]; if (!T) return; if (T.ht[n].rchild = = 0) { /* Why ? */ cd[codeLen] = '\0'; strcpy(HC[n], cd); } else { cd[codeLen++] = '0'; TraverseHuffman (T, HC, T.ht[n].lchild); codeLen--; cd[codeLen++] = '1'; TraverseHuffman (T, HC, T.ht[n].rchild); Prof. Q. Wang

unsigned int parent, lchild, rchild; } HTNode, *HuffmanTree; typedef struct { unsigned int weight; unsigned int parent, lchild, rchild; } HTNode, *HuffmanTree; typedef char ** HuffmanCode; C Program in textbook void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n) { if (n<=1) return; m=2*n-1; HT= (HuffmanTree) malloc ((m+1)*sizeof(HTNode)); for (p=HT, i=1; i<=n; ++i, ++p, ++w) *p={*w, 0, 0, 0}; for (; i<=m; ++i, ++p) *p={0,0,0,0}; for (i=n+1; i<=m; ++i) { Select (HT, i-1, s1, s2); HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1; HT[i].rchild=s2; HT[i].weight=HT[s1].weight+HT[s2].weight; } Prof. Q. Wang

HC=(HuffmanCode) malloc ((n+1)*siezof(char *)); cd=(char *) malloc (n*sizeof(char)); cd[n-1]=“\0”; for (i=1; i<=n; ++i) { start = n-1; for ( c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent) if (HT[f].lchild == c) cd[--start]=“0”; else cd[--start]=“1”; HC[i]=(char *) malloc ((n-start)*sizeof(char)); strcpt (HC[i], &cd[start]); } free (cd); Prof. Q. Wang

Assignments (5) 提交算法和程序 6.70, 6.42, 6.51, 6.65 自己练习与思考 6.14, 6.26, 6.27, 6.28, 6.31, 6.56, 6.57, 6.62 Prof. Q. Wang

Practical (3) P149 5.2 哈夫曼编/译码器 完成Huffman 编码的译码过程。即输入一个码串,请翻译成相应的字符串。要求有编码过程和解码过程。 Prof. Q. Wang

Conclusion Definition and notations of Tree and Forest (树和森林的定义) Notations and representation of binary tree (二叉树的概念及存储表示) Binary tree traversal (二叉树的遍历) Threading binary tree (线索二叉树) Reconstruction of binary tree (二叉树的构造) Transformation among Tree, Forest and binary tree (树、森林和二叉树的转换) Huffman tree and Huffman coding (哈夫曼树和哈夫曼编码) Prof. Q. Wang

其他问题 树与等价问题 回溯法和树的遍历 Tree的计数 Prof. Q. Wang

Tree的计数 具有n个结点有不同形态的树(有序树)的数目Tn和具有n-1个结点互不相似的二叉树的数目相同,即 Why? Prof. Q. Wang

The End! Prof. Q. Wang