树和森林的概念 二叉树 二叉树遍历 二叉树的计数 线索化二叉树 堆...

140
树树树树树树树 树树树 树树树树树 树树树树树树 树树树树树树 树树树树 树树树树

description

第六章 树与森林. 树和森林的概念 二叉树 二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树. 树和森林的概念. 树的定义 树是由 n ( n  0) 个结点组成的有限集合。如果 n = 0 ,称为空树;如果 n > 0 ,则  有一个特定的称之为 根 (root) 的结点,它只有直接后继,但没有直接前驱;  除根以外的其它结点划分为 m ( m  0) 个互不相交的有限集合 T 0 , T 1 , …, T m -1 ,每个集合又是一棵树 , 并且称之为根的 子树 。. 树的特点. - PowerPoint PPT Presentation

Transcript of 树和森林的概念 二叉树 二叉树遍历 二叉树的计数 线索化二叉树 堆...

Page 1: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

树和森林的概念 二叉树 二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

Page 2: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

树和森林的概念树和森林的概念树的定义树的定义 树是由树是由 nn ( (nn 0) 0) 个结点组成的有限个结点组成的有限集合。如果 集合。如果 nn = 0 = 0 ,称为空树;如果 ,称为空树;如果 nn > 0 > 0 ,,则则 有一个特定的称之为有一个特定的称之为根根 (root)(root) 的结点,的结点,它只有直接后继,但没有直接前驱;它只有直接后继,但没有直接前驱; 除根以外的其它结点划分为 除根以外的其它结点划分为 mm ( (mm 0) 0) 个互不相交的有限集合个互不相交的有限集合 TT00, , TT11, …, , …, TTmm-1-1 ,,每个集合又是一棵树每个集合又是一棵树,,并且称之为根的并且称之为根的子树子树。。

Page 3: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

树的特点树的特点 每棵子树的根结点有且仅有一个直接前每棵子树的根结点有且仅有一个直接前驱,但可以有驱,但可以有 00 个或多个直接后继。个或多个直接后继。

0 层

1 层

3 层

2 层height

= 3

A

C

G

B D

E F

K L

H

M

I J

Page 4: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 层

1 层

3 层

2 层height

= 3

A

C

G

B D

E F

K L

H

M

I J

结点结点 结点的度结点的度 分支结点分支结点 叶结点叶结点

子女子女 双亲双亲 兄弟兄弟

祖先祖先 子孙子孙 结点层次结点层次

树的度树的度 树树高度高度 森林森林

Page 5: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> class Tree {// 对象 : 树是由 n(≥0) 个结点组成的有限集// 合。在类界面中的 position 是树中结点的// 地址。在顺序存储方式下是下标型 , 在链// 表存储方式下是指针型。 Type 是树结点中存放数据的类型。public: Tree ( ); ~Tree ( );

树的抽象数据类型树的抽象数据类型

Page 6: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

position Root ( ); BuildRoot ( const Type& value ); position FirstChild ( position p ); position NextSibling ( position p, position v ); position Parent ( position p ); Type GetData ( position p );

int InsertChild ( const position p, const Type &value );

int DeleteChild ( position p, int i );}

Page 7: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树二叉树 (Binary Tree)(Binary Tree)

二叉树的定义二叉树的定义

二叉树的五种不同形态二叉树的五种不同形态

一棵二叉树是结点的一个有限集一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树的、点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。互不相交的二叉树组成。

L LR R

Page 8: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

性质性质 11 若二叉树的层次从若二叉树的层次从 00 开始开始 , , 则在二则在二叉树的第 叉树的第 i i 层最多有 层最多有 22i i 个结点。个结点。 ((ii 0) 0)

[[ 证明用数学归纳法证明用数学归纳法 ]]

性质性质 22 高度为高度为 hh 的二叉树最多有 的二叉树最多有 22h+h+11--11

个结点。个结点。 ((hh --11))

[[ 证明用求等比级数前证明用求等比级数前 kk 项和的公式项和的公式 ]]

2200 + 2 + 211 + 2 + 222 + + … + 2… + 2hh = 2 = 2hh+1+1--11

二叉树的性质二叉树的性质

Page 9: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

性质性质 33 对任何一棵二叉树对任何一棵二叉树 , , 如果其如果其叶结叶结点点有有 nn0 0 个个 , , 度度为为 22 的的非叶结点非叶结点有有 nn2 2 个个 ,,

则有则有 nn00 == nn22 ++ 11

证明:证明:若设若设度度为为 11 的结点有 的结点有 nn11 个,总结个,总结点个数为 点个数为 nn ,总边数为 ,总边数为 ee ,则根据二叉,则根据二叉树的定义,树的定义, nn = = nn00 + + nn11 + + nn22 ee = 2 = 2nn22 + + nn1 1 = = nn -- 1 1

因此,有因此,有 22nn22 + + nn11 = = nn00 + + nn11 + + nn22 -- 1 1

nn22 = = nn00 -- 1 1 nn00 = = nn22 + 1 + 1

Page 10: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

定义定义 11 满二叉树 满二叉树 (Full Binary Tree)(Full Binary Tree)

定义定义 22 完全二叉树 完全二叉树 (Complete Binary Tre(Complete Binary Tre

e)e) 若设二叉树的高度为若设二叉树的高度为 hh ,则共有,则共有 hh+1+1层。除第 层。除第 h h 层外,其它各层 层外,其它各层 (0 (0 hh--1) 1) 的结点数都达到最大个数,第 的结点数都达到最大个数,第 h h 层从右层从右向左连续缺若干结点,这就是完全二叉树。向左连续缺若干结点,这就是完全二叉树。

Page 11: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

性质性质 44 具有具有 nn ((n n 0) 0) 个结点的完全二个结点的完全二叉树的高度为 叉树的高度为 loglog22((nn+1)+1) --1 1

证明:证明:设完全二叉树的高度为 设完全二叉树的高度为 hh ,则有,则有 22hh -- 1 < 1 < nn 2 2hh+1+1 -- 1 1

变形 变形 22hh < < n+n+1 1 2 2hh+1+1

取对数 取对数 hh < log < log22((nn+1) +1) hh+1+1

因此有 因此有 loglog22((nn+1)+1) = = hh+1+1

hh = = loglog22((nn+1)+1) --1 1

上面 h 层结点数 第 h 层结点数

Page 12: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

性质性质 55 如将一棵有如将一棵有 nn 个结点的完全二叉树个结点的完全二叉树自顶向下,同一层自左向右连续给结点编自顶向下,同一层自左向右连续给结点编号号 0, 1, 2, …, 0, 1, 2, …, nn--11 ,则有以下关系:,则有以下关系: 若若 i = i = 0, 0, 则 则 i i 无双亲无双亲 若若 i i > 0, > 0, 则 则 i i 的双亲为的双亲为 ((i i --1)/21)/2 若若 2*2*ii+1 <+1 < n n, , 则 则 i i 的左子女为 的左子女为 2*2*ii+1+1 ,, 若若 2*2*ii+2 < +2 < nn, , 则 则 i i 的右子女为的右子女为 2*2*ii+2+2 若 若 i i 为偶数为偶数 , , 且且 ii != 0, != 0, 则其左兄弟为则其左兄弟为 ii--1, 1, 若若 i i 为奇数为奇数 , , 且且 ii != != nn-1, -1, 则其右兄弟为则其右兄弟为 ii+1 +1

0

1 2

3

7

4 5 6

8 9

Page 13: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树的抽象数据类型二叉树的抽象数据类型template <class Type> class BinaryTree {public: BinaryTree ( ); // 构造函数 BinaryTree ( BinTreeNode<Type> * lch, BinTreeNode<Type> * rch, Type item ); // 构造以 item 为根, lch 和 rch 为左、右 // 子树的二叉树 int IsEmpty ( ); // 判二叉树空否? BinTreeNode<Type> * Parent ( ); // 双亲

Page 14: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

BinTreeNode<Type> * LeftChild ( ); // 取左子女结点地址 BinTreeNode<Type> * RightChild ( ); // 取右子女结点地址 int Insert ( const Type& item ); // 插入 int Find ( const Type &item ) const; // 搜索 Type GetData ( ) const; // 取得结点数据 BinTreeNode<Type> *GetRoot ( ) const; // 取根结点地址}

Page 15: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

完全二叉树 一般二叉树的顺序表示 的顺序表示

二叉树的顺序表示二叉树的顺序表示

0

0 1 2 3 4 5 6 7 8 9

9

0 1 2 3 4 5 6 7 8 9

1

3

7 8 9

4 5 6

2

0

1 2

543

6 7 8

Page 16: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树的链表表示二叉树的链表表示

leftChild data rightChild

data

leftChild rightChild

二叉链表

Page 17: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树的链表表示二叉树的链表表示

leftChild data parent rightChild

parent

data

leftChild rightChild

三叉链表

Page 18: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树链表表示的示例二叉树链表表示的示例

A A A

B B B

CCC D D D

FFFE E E

root root root

二叉树 二叉链表 三叉链表

Page 19: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉链表的静态结构二叉链表的静态结构

A

B

C D

FE

rootdata parent leftChild rightChild

012345

AA --1 1 1 1 --11BB 0 2 3 0 2 3CC 1 1 --1 1 --11DD 1 4 5 1 4 5EE 3 3 --1 1 --11FF 3 3 --1 1 --11

Page 20: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> class BinaryTree;

template <class Type> Class BinTreeNode {friend class BinaryTree<Type>;private: Type data; BinTreeNode<Type> * leftChild; BinTreeNode<Type> * rightChild; public: BinTreeNode ( ) : leftChild (NULL),

rightChild (NULL) { }

二叉树的类定义二叉树的类定义

Page 21: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

BinTreeNode ( Type item, BinTreeNode<Type> *left = NULL,

BinTreeNode<Type> *right = NULL ) : data (item), leftChild (left), rightChild (right) { } Type GetData ( ) const { return data; } BinTreeNode<Type> * GetLeft ( ) const { return leftChild; } BinTreeNode<Type> * GetRight ( ) const { return rightChild; } void SetData ( const Type& item ) { data = item; }

Page 22: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

void SetLeft ( BinTreeNode <Type> * L ) { leftChild = L; } void SetRight ( BinTreeNode <Type> * R ) { rightChild = R; }};

template <class Type> class BinaryTree {private: BinTreeNode <Type> *root; Type RefValue; void CreateBinTree ( ifstream& in, BinTreeNode<Type> * & current );

Page 23: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

BinTreeNode<Type> * Parent ( BinTreeNode<Type> * start, BinTreeNode<Type> * current ); int Insert ( BinTreeNode<Type> * & current, const Type &item ); // 插入 void Traverse ( BinTreeNode<Type> *current, ostream &out ) const // 遍历 int Find ( BinTreeNode<Type> *current, const Type &item ) const // 搜索 void destroy ( BinTreeNode<Type> * current );

// 删除

Page 24: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

public: virtual BinaryTree ( ) : root (NULL) { } virtual BinaryTree ( Type value ) : RefValue (value), root (NULL) { } virtual ~BinaryTree ( ) { destroy ( root ); } virtual int IsEmpty ( ) // 判树空否 { return root == NULL ? 1 : 0; } virtual BinTreeNode <Type> * Parent ( BinTreeNode <Type> *current ) { return root == NULL || root == current ? NULL : Parent ( root, current ); } // 找 current 结点的双亲

Page 25: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

virtual BinTreeNode<Type> * LeftChild ( BinTreeNode<Type> *current )

// 取 current 结点的左子女 { return root != NULL ? current->leftChild : NULL; } virtual BinTreeNode<Type> * RightChild ( BinTreeNode<Type> *current ) // 取 current 结点的右子女 { return root != NULL ? current->rightChild : NULL; } virtual int Insert ( const Type& item);

Page 26: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

virtual BinTreeNode<Type> * Find ( const Type& item ) const; // 搜索 BinTreeNode<Type> *GetRoot ( ) const { return root; } // 取根 friend istream& operator >> (filename, BinaryTree<Type>& Tree)

// 重载操作:输入 friend ostream& operator << (ostream&out, BinaryTree<Type>& Tree) // 重载操作:输出}

Page 27: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树部分成员函数的实现二叉树部分成员函数的实现

template<class Type> void BinaryTree<Type> :: destroy ( BinTreeNode<Type> *current ) { if ( current != NULL ) { destroy ( current -> leftChild ); destroy ( current -> rightChild ); delete current; }}

Page 28: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template<class Type> void BinaryTree <Type> :: CreateBinTree ( ifstream& in, BinTreeNode<Type> * & current ) {// 私有函数 : 以递归方式建立二叉树。// 输入结点值的顺序必须对应二叉树结点前// 序遍历的顺序。并约定以输入序列中不可// 能出现的值作为空结点的值以结束递归 ,// 此值在 RefValue 中。例如用“ @” 或用“ -1”// 表示字符序列或正整数序列空结点。 Type item; if ( !in.eof ( ) ) {

Page 29: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

如图所示的二叉树的前序遍历顺序为A B C @ @ D E @ G @ @ F @ @ @

A

B

C D

E

G

F@ @

@

@ @

@ @

@

Page 30: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

in >> item; // 读入根结点的值 if ( item != RefValue ) { current = new BinTreeNode<Type> ( item ); // 建立根结点 if ( current == NULL ) { cerr << “ 存储分配错 !” << endl; exit (1); } CreateBinTree ( in, current->leftChild ); CreateBinTree ( in, current->rightChild ); } else current = NULL; // 封闭叶结点 }}

Page 31: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> BinTreeNode <Type> * BinaryTree <Type> :: Parent ( BinTreeNode <Type> * start, BinTreeNode <Type> * cuurent ) { if ( start == NULL ) return NULL; if ( start->leftChild == current || start->rightChild == current ) return start; BinTreeNode <Type> *p; if (( p = Parent ( start->leftChild, current )) != NULL ) return p; else return Parent(start->rightChild, current);}

Page 32: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> void BinaryTree<Type> :: Traverse ( BinTreeNode <Type> *current, ostream &out ) const {// 私有函数 : 搜索并输出根为 current 的二叉树 if ( current != NULL ) { out << current->data << ‘ ’; Traverse ( current->leftChild, out ); Traverse ( current->rightChild, out ); }}

Page 33: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template<class Type> istream& operator >> ( filename, BinaryTree<Type> & Tree ) {// 建立一棵二叉树 Tree 。 in 是输入流对象 Type item; ifstream fin (filename, // 打开文件 ios :: in | ios :: nocreate ); if ( !fin ) { cerr << “ 文件未发现 !” << endl; exit (1); } CreateBinTree ( ifstream& fin, root ); fin.close ( ); // 关闭文件}

Page 34: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> ostream& operator << ( ostream& out, BinaryTree<Type> &Tree ) { out << “ 二叉树的前序遍历 .\n"; Tree.Traverse ( Tree.root, out ); out << endl; return out;}

Page 35: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树遍历二叉树遍历 树的遍历就是按某种次序访问树中树的遍历就是按某种次序访问树中的结点,要求每个结点访问一次且仅访问一的结点,要求每个结点访问一次且仅访问一次。次。 设设访问根结点访问根结点记作 记作 VV 遍历根的左子树遍历根的左子树记作 记作 LL 遍历根的右子树遍历根的右子树记作 记作 RR 则可能的遍历次序有则可能的遍历次序有 前序 前序 VLR VLR 镜像 镜像 VRLVRL 中序 中序 LVR LVR 镜像 镜像 RVLRVL

后序 后序 LRV LRV 镜像 镜像 RLVRLV

Page 36: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

中序遍历二叉树算法的框架是:中序遍历二叉树算法的框架是: 若二叉树为空,则空操作;若二叉树为空,则空操作; 否则否则

中序遍历左子树 中序遍历左子树 (L)(L) ;; 访问根结点 访问根结点 (V)(V) ;; 中序遍历右子树 中序遍历右子树 (R)(R) 。。遍历结果遍历结果 aa + + bb * * cc -- dd -- ee / / ff

中序遍历 中序遍历 (Inorder Traversa(Inorder Traversal)l)

--

--

//++

**aa

bb

cc dd

ee ff

Page 37: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树递归的中序遍历算法二叉树递归的中序遍历算法template <class Type> void BinaryTree <Type> :: InOrder ( BinTreeNode <Type> *current ) { if ( current != NULL ) { InOrder ( current->leftChild ); cout << current->data; InOrder ( current->rightChild ); }}

Page 38: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

前序遍历二叉树算法的框架是:前序遍历二叉树算法的框架是: 若二叉树为空,则空操作;若二叉树为空,则空操作; 否则否则

访问根结点 访问根结点 (V)(V) ;; 前序遍历左子树 前序遍历左子树 (L)(L) ;; 前序遍历右子树 前序遍历右子树 (R)(R) 。。遍历结果遍历结果 -- + + aa * * bb -- c dc d / / e fe f

前序遍历 前序遍历 (Preorder Traversal)(Preorder Traversal)

--

--

//++

**aa

bb

cc dd

ee ff

Page 39: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树递归的前序遍历算法二叉树递归的前序遍历算法template <class Type> void BinaryTree<Type> ::PreOrder ( BinTreeNode <Type> * current ) { if ( current != NULL ) { cout << current->data; PreOrder ( current->leftChild ); PreOrder ( current->rightChild ); }}

Page 40: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

后序遍历二叉树算法的框架是:后序遍历二叉树算法的框架是: 若二叉树为空,则空操作;若二叉树为空,则空操作; 否则否则

后序遍历左子树 后序遍历左子树 (L)(L) ;; 后序遍历右子树 后序遍历右子树 (R)(R) ;; 访问根结点 访问根结点 (V)(V) 。。遍历结果遍历结果 a b c da b c d -- * + * + e fe f / / --

后序遍历 后序遍历 (Postorder Traversa(Postorder Traversal)l)

--

--

//++

**aa

bb

cc dd

ee ff

Page 41: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树递归的后序遍历算法二叉树递归的后序遍历算法

template <class Type> void BinaryTree <Type> ::PostOrder ( BinTreeNode <Type> * current ) { if ( current != NULL ) { PostOrder ( current->leftChild ); PostOrder ( current->rightChild ); cout << current->data; }}

Page 42: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

利用二叉树利用二叉树后序遍历后序遍历计算二叉树结点个数计算二叉树结点个数及二叉树的高度及二叉树的高度template <class Type> int BinaryTree<Type> :: Count ( const BinTreeNode <Type> * t ) const { if ( t == NULL ) return 0; else return 1 + Count ( t->leftChild ) + Count ( t->rightChild );}

应用二叉树遍历的事例应用二叉树遍历的事例

Page 43: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> int BinaryTree<Type> :: Height ( const BinTreeNode <Type> * t ) const { if ( t == NULL ) return -1; else return 1 + Max ( Height ( t->leftChild ), Height ( t->rightChild ) );}

Page 44: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

利用栈的前序遍历非递归算法利用栈的前序遍历非递归算法a

b c

d ec

dc c

访问a

进栈c

左进b

访问b

进栈d

左进空

退栈d

访问d

左进空

退栈c

访问c

左进e

访问e

左进空

栈空结束

Page 45: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> void BinaryTree<Type> :: PreOrder( ) { stack <TreeNode<Type>* > S; BinTreeNode<Type> * p = root; // 初始化 S.Push ( NULL ); while ( p != NULL ) { cout << p->data << endl; if ( p->rightChild != NULL ) S.Push ( p->rightChild ); // 预留右子树指针在栈中

利用栈的前序遍历非递归算法利用栈的前序遍历非递归算法

Page 46: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

if ( p->leftChild != NULL ) p = p->leftChild; // 进左子树 else { p = S.getTop( ); S.Pop( ); } // 左子树为空 , 进右子树 }}

利用栈的中序遍历非递归算法利用栈的中序遍历非递归算法template <class Type> void BinaryTree<Type> :: InOrder ( ) { stack <TreeNode<Type>* > S;

Page 47: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

利用栈的中序遍历非递归算法利用栈的中序遍历非递归算法a

b c

d e

ba a

da a

左空 退栈访问

左空 退栈访问

退栈访问

左空

ec

退栈访问c c

右空退栈访问 栈空结束

Page 48: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

BinTreeNode<Type> * p = root; // 初始化 do { while ( p != NULL ) { S.Push(p); p = p->leftChild; } if ( !S.IsEmpty( ) ) { // 栈非空 p = S.getTop( ); S.Pop( ); // 退栈 cout << p->data << endl; // 访问根 p = p->rightChild; // 向右链走 } } while ( p != NULL || !S.IsEmpty( ) );}

Page 49: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

利用栈的后序遍历非递归算法利用栈的后序遍历非递归算法后序遍历时使用的栈的结点定义后序遍历时使用的栈的结点定义template <class Type> struct stkNode { BinTreeNode<Type> *ptr; enum tag{ L, R }; // 该结点退栈标记 stkNode( BinTreeNode<Type>* N = NULL ) : ptr (N), tag ( L ) { } // 构造函数};

根结点的 tag = L ,表示从左子树退出 , 访问右子树。 tag = R, 表示从右子树退出 , 访问根。

ptr tag{L,R}

Page 50: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

a

b c

d eaL

bLaL

bRaL

dLbRaL

dRbRaL

bRaL aL aR

eLcLaR

eRcLaR

cLaR

cRaR aR

Page 51: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> void BinaryTree<Type> :: PostOrder ( ) { stack <stkNode<Type> > S; stkNode<Type> w; BinTreeNode<Type> * p = root; do {

while ( p != NULL ) { // 向左子树走 w.ptr = p; w.tag = L; st.Push ( w );

p = p->leftChild;

} int continue = 1; // 继续循环标记

Page 52: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

while ( continue && !S.IsEmpty( ) ) { w = st.getTop ( ); st.Pop ( ); p = w.ptr; switch ( w.tag ) { // 判断栈顶 tag 标记

case L : w.tag = R; st.Push ( w ); continue = 0;

p = p->rightChild; break; case R : cout << p->data << endl;

} }} while ( p != NULL || !S.IsEmpty( ) );

cout << endl; }

Page 53: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

二叉树的计数二叉树的计数 由二叉树的前序序列和中序序列可唯由二叉树的前序序列和中序序列可唯一地确定一棵二叉树。一地确定一棵二叉树。 例 前序序列 例 前序序列 { ABHFDECKG } { ABHFDECKG } 和和中序序列 中序序列 { HBDFAEKCG }, { HBDFAEKCG }, 构造二叉树构造二叉树过程如下:过程如下:

HBDF EKCG

A

EKCG

A

B

H DF

Page 54: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

KCG

前序序列 前序序列 { ABHFDECKG{ ABHFDECKG } }

EKCG

A

B

H DF

EKCG

A

B

H F

D

E

A

B

H F

D

E

A

B

H F

D

C

K G

Page 55: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

如果前序序列固定不变,给出不同的中如果前序序列固定不变,给出不同的中序序列,可得到不同的二叉树。序序列,可得到不同的二叉树。

6

1

2

3 4

5

7

8 9

6

1

2

3 7

5

8

4 9

固定前序排列固定前序排列 ,, 选择所有可能的中序排选择所有可能的中序排列列 ,, 可以构造多少种不同的二叉树?可以构造多少种不同的二叉树?

Page 56: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

例如例如 , , 有 有 3 3 个数据 个数据 { 1, 2, 3 }{ 1, 2, 3 } ,可得 ,可得 5 5 种不同的二叉树。它们的前序排列均为种不同的二叉树。它们的前序排列均为 123 ,中序序列可能是 中序序列可能是 123123 ,, 132132 ,, 213213 ,,231231 ,, 321321 。。

1

2

3

1

2

3

1

2 3

1

2

3

1

2

3

Page 57: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

有有 00 个个 , 1, 1 个个 , 2, 2 个个 , 3, 3 个结点的不同二叉个结点的不同二叉树如下树如下

b0 =1 b1 =1 b2 =2b3 =5

b4 =14

Page 58: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

!!

)!(2

1

1

1

12 nn

n

nnb C

n

nn

计算具有 计算具有 n n 个结点的不同二叉树的棵数个结点的不同二叉树的棵数

CatalanCatalan 函数函数bbii bbn-i-n-i-11

1

1

01

n

iinin bbb

Page 59: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

线索化二叉树 线索化二叉树 (Threaded Binary Tree)(Threaded Binary Tree)

线索线索 (Thread)(Thread)

增加 增加 Pred Pred 指针和 指针和 Succ Succ 指针的二叉指针的二叉树树

Page 60: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

线索化二叉树及其二叉链表表示线索化二叉树及其二叉链表表示

LeftThreadLeftThread=0=0, , LeftChildLeftChild 为为左子女指针左子女指针LeftThreadLeftThread=1=1, , LeftChildLeftChild 为为前驱线索前驱线索RightThreadRightThread=0=0, , RightChildRightChild 为为右子女指针右子女指针RightThreadRightThread=1=1, , RightChildRightChild 为为后继指针后继指针

LeftChildLeftChild RightChildRightChilddatadata

LeftThreadLeftThread RightThreadRightThread

Page 61: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

中序线索化二叉树的类定义中序线索化二叉树的类定义template <class Type> class ThreadTree;

Page 62: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> class ThreadNode { friend class ThreadTree<Type>;private: int leftThread, rightThread; ThreadNode<Type> *leftChild, *rightChild; Type data; public: ThreadNode ( const Type item ) : data (item), leftChild (NULL), rightChild (NULL), leftThread (0), rightThread (0) { }};

Page 63: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> class ThreadTree {private: ThreadNode<Type> * root; // 根 InThread ( ThreadNode <Type> * current, ThreadNode <Type> * pre ); // 建树public: ThreadTree ( ) : root (NULL) { }; // 构造函数 ThreadNode<Type> * First ( ThreadNode <Type> * current ); ThreadNode<Type> * Last ( ThreadNode <Type> * current );

Page 64: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

ThreadNode<Type> * Next ( ThreadNode <Type> * current ); ThreadNode<Type> * Prior ( ThreadNode <Type> * current ); …………}

Page 65: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

通过中序遍历建立中序线索化二叉树通过中序遍历建立中序线索化二叉树template <class Type> void ThreadTree<Type> ::InThread ( ThreadNode<Type> * current, ThreadNode<Type> *& pre ) { if ( current != NULL ) {

InThread ( current->leftChild, pre ); // 递归 , 左子树线索化

if ( current->leftChild == NULL ) { current->leftChild = pre; current->leftThread = 1; } // 建立当前结点的前驱线索

Page 66: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

if ( pre != NULL && pre->rightChild == NULL ) { pre->rightChild = current; pre->rightThread = 1; } // 建立前驱结点的后继线索

pre = current; // 前驱跟上当前指针InThread ( current->rightChild, pre ); // 递归 , 右子树线索化

}}

Page 67: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> voidThreadTree<Type> :: CreateInThread ( ) { ThreadNode<Type> *pre = NULL;

// 前驱指针 if ( root != NULL ) { // 非空二叉树 , 线索化

InThread ( root, pre ); // 中序遍历线索化二叉树

pre->rightChild = NULL; pre->rightThread = 1; // 后处理 , 中序最后一个结点 }}

Page 68: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 A 0

0 B 0 0 C 0

0 D 0 0 E 0

rootpre == NULL current

Page 69: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 A 0

1 B 0 0 C 0

0 D 0 0 E 0

rootpre == NULL

current

Page 70: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 A 0

1 B 0 0 C 0

1 D 0 0 E 0

root

pre

current

Page 71: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 A 0

1 B 0 0 C 0

1 D 1 0 E 0

root

pre

current

Page 72: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 A 0

1 B 0 0 C 0

1 D 1 1 E 0

rootpre

current

Page 73: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 A 0

1 B 0 0 C 0

1 D 1 1 E 1

root

pre

current

Page 74: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

0 A 0

1 B 0 0 C 1

1 D 1 1 E 1

root

pre后处理

Page 75: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

if (p->rightThread ==1) if (p->rightChild != NULL) 后继为 p->rightChild else 无后继 else //p->rightThread != 1 if (p->rightChild != NULL) 后继为当前结点右子树 的中序下的第一个结点 else 出错情况

寻找当前结点在中序下的后继寻找当前结点在中序下的后继

AA

BB

DD EE

CC

FF

HH II

KK

GG

JJ

Page 76: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

if (current->leftThread==1) if (current->leftChild != NULL) 前驱为 current->leftChild else 无前驱 else //current->leftThread==0 if (current->leftChild != NULL) 前驱为当前结点左子树 中序下的最后一个结点 else 出错情况

寻找当前结点在中序下的前驱寻找当前结点在中序下的前驱

AA

BB

DD EE

CC

FF

HH II

KK

GG

JJ

LL

Page 77: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

在中序线索化二叉树中部分成员函数的实现在中序线索化二叉树中部分成员函数的实现

template <class Type> ThreadNode<Type> * ThreadTree <Type> :: First ( ThreadNode<Type> * current ) {// 函数返回以 *current 为根在线索化二叉树中// 的中序序列下的第一个结点 ThreadNode<Type> * p = current; while ( p->leftThread == 0 ) p = p->leftChild; // 最左下的结点 return p;}

Page 78: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> ThreadNode<Type> * ThreadTree <Type> :: Next ( ThreadNode<Type> * current ) {// 函数返回在线索化二叉树中结点 *current 在// 中序下的后继结点 ThreadNode<Type> *p = current->rightChild; if ( current->rightThread == 0 ) return First ( p ); //rightThread == 0, 表示有右子女 else return p;

//rightThread == 1, 直接返回后继线索}

Page 79: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> void ThreadTree <Type> :: Inorder ( ) {// 线索化二叉树的中序遍历 ThreadNode<Type> * p; for ( p = First ( ); p != NULL; p = Next ( ) ) cout << p->data << endl;}

Page 80: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

前序线索化二叉树前序线索化二叉树

前序序列前序序列 A B D C EA B D C E

在前序线索化二叉树中在前序线索化二叉树中 寻找当前结点的后继寻找当前结点的后继

p->leftThread==1?

前驱线索 = 左子女pp->->rightChildrightChild == NULL== NULL

后继为p->leftChild

= 无后继 后继为

p->rightChild

A

B C

ED

Page 81: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

后序线索化二叉树后序线索化二叉树

后序序列后序序列 D B E C AD B E C A

在前序线索化二在前序线索化二叉树中寻找当前叉树中寻找当前结点的后继结点的后继

p->rightThread==1?

后继线索 = 右子女

后继为后继为 qq 的右子树中的右子树中后序下第一个结点后序下第一个结点

后继为p->leftChild

=

无后继

后继为 q

A

B C

ED

q=p->parentq==NULL?

Q->rightThread==1 ||q->rightChild==p?

=

Page 82: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

堆 堆 ( Heap )( Heap )

template <class Type> class MinPQ {public: Virtual void Insert ( const Type & ) = 0; Virtual Type *RemoveMin ( Type & ) = 0;}

最小优先级队列类的定义

优先级队列优先级队列 每次出队列的是优先权最高的元素每次出队列的是优先权最高的元素

Page 83: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

完全二叉树完全二叉树 数组表示数组表示KKii KK22ii+1+1 && KKii KK22ii+2+2

完全二叉树完全二叉树 数组表示数组表示KKii KK22ii+1+1 && KKii KK22ii+2+2

堆的定义堆的定义09

09

87

87

78

7845 45

65

65 31

3153 23

23

53

17

17

Page 84: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

最小堆的类定义最小堆的类定义#define DefaultSize 10;template <class Type> class MinHeap : public MinPQ <Type> {private: Type * heap; // 存放最小堆元素的数组 int CurrentSize; // 最小堆当前元素个数 int MaxHeapSize; // 最多允许元素个数 void FilterDown ( int i, int m ); // 从 i 到 m 自顶向下进行调整成为最小堆

Page 85: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

void FilterUp ( int i ); // 从 i 到 0 自底向上进行调整成为最小

堆public: MinHeap ( int sz ); // 构造函数 : 建立空堆 MinHeap ( Type arr[ ], int n ); // 构造函数 ~MinHeap ( ) { delete [ ] heap; } const MinHeap<Type>& operator = ( const MinHeap& R ); int Insert ( const Type& x ); // 插入 int RemoveMin ( Type& x ); // 删除

Page 86: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

int IsEmpty ( ) const // 判堆空否 { return CurrentSize == 0; } int IsFull ( ) const // 判堆满否 { return CurrentSize == MaxHeapSize; } void MakeEmpty ( ) { CurrentSize = 0; }}

堆的建立堆的建立template <class Type> MinHeap <Type> ::MinHeap ( int maxSize ) {// 根据给定大小 maxSize, 建立堆对象

Page 87: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

MaxHeapSize = DefaultSize < maxSize ? maxSize : DefaultSize; // 确定堆的大小 heap = new Type [MaxHeapSize]; if ( heap == NULL ) { cerr << “ 存储分配错 !” << endl; exit(1); } CurrentSize = 0; }

template <class Type> MinHeap <Type> :: MinHeap ( Type arr[ ], int n ) {// 根据给定数组中的数据和大小 , 建立堆对象

Page 88: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

MaxHeapSize = DefaultSize < n ? n : DefaultSize; heap = new Type [MaxHeapSize]; if ( heap == NULL ) { cerr << “ 存储分配错 !” << endl; exit(1); } heap = arr; // 数组传送 CurrentSize = n; // 当前堆大小 int currentPos = (CurrentSize-2)/2; // 找最初调整位置 :最后的分支结点号

Page 89: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

while ( currentPos >= 0 ) { // 从下到上逐步扩大 ,形成堆 FilterDown ( currentPos, CurrentSize-1 ); // 从 currentPos 开始 ,到 CurrentSize止 , // 调整 currentPos--; }}

Page 90: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

自下向上逐步调整为最小堆自下向上逐步调整为最小堆

将一组用数组存放的任意数据调整成堆将一组用数组存放的任意数据调整成堆

53 53

17 1778 78

09

23 45 65 87

i

09

23

45 65 87

currentPos = i = 3 currentPos = i = 2

i

Page 91: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

53 53

17

1778 7809

23

45

65

87

i

09

23

45

65

87

currentPos = i = 1

i

Page 92: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

53

53

17 1778 78

09

23

45

65

87

i

09

23

45

65

87

currentPos = i = 0

i

09

Page 93: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

53

53

17 17

78 78

09

23

45

65

87

i

09

23 45

65

87i17

Page 94: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

最小堆的向下调整算法最小堆的向下调整算法template <class Type> void MinHeap<Type> :: FilterDown ( int start, int EndOfHeap ) { int i = start, j = 2*i+1; // j 是 i 的左子女 Type temp = heap[i]; while ( j <= EndOfHeap ) { if ( j < EndOfHeap && heap[j].key > heap[j+1].key ) j++; // 两子女中选小者

Page 95: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

if ( temp.key <= heap[j].key ) break; else { heap[i] = heap[j]; // 下面的上浮 i = j; j = 2*j+1; // 向下滑动 } } heap[i] = temp;}

堆的插入堆的插入template <class Type> int MinHeap<Type> :: Insert ( const Type &x ) {

Page 96: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

// 在堆中插入新元素 x if ( CurrentSize == MaxHeapSize ) // 堆满 { cerr << " 堆已满 " << endl; return 0; } heap[CurrentSize] = x; // 插在表尾 FilterUp (CurrentSize); // 向上调整为堆 CurrentSize++; // 堆元素增一 return 1;}

最小堆的向上调整算法最小堆的向上调整算法

Page 97: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> void MinHeap<Type> :: FilterUp ( int start ) {// 从 start 开始 ,向上直到 0, 调整堆 int j = start, i = (j-1)/2; // i 是 j 的双亲 Type temp = heap[j]; while ( j > 0 ) { if ( heap[i].key <= temp.key ) break; else { heap[j] = heap[i]; j = i; i = (i -1)/2;} } heap[j] = temp;}

Page 98: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

53

17 17

78 78

09

23 45

65

87

i

09

23 45

65

87

j

11

在堆中插入新元素 11

53j

11

23

i

最小堆的向上调整最小堆的向上调整

Page 99: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

53

17 11

78 78

09

45

65

87

09

23 45

65

87

j

11

53 23

i

23

17

j

i

Page 100: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

最小堆的删除算法最小堆的删除算法template <class Type> int MinHeap <Type> ::RemoveMin ( Type &x ) { if ( !CurrentSize ) { cout << “ 堆已空 " << endl; return 0; } x = heap[0]; // 最小元素出队列 heap[0] = heap[CurrentSize-1]; CurrentSize--; // 用最小元素填补 FilterDown ( 0, CurrentSize-1 ); // 调整 return 1;}

Page 101: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

树的存储表示树的存储表示 广义表表示广义表表示

树的广义表表示树的广义表表示 ( 结点的 utype域没有画出 )

树与森林树与森林

A

B C D

E F G

A

B E F

C

D G

Page 102: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

双亲表示双亲表示

A

B C D

E F G

data

parent

A B C D E F G

-1 0 0 0 1 1 3

0 1 2 3 4 5 6

左子女左子女 -- 右兄弟表示右兄弟表示法法第一种解决方案第一种解决方案

data data child1 child2 child3 childd

Page 103: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

第二种解决方案第二种解决方案

树的左子女 树的左子女 -- 右兄弟表右兄弟表示示

data data firstChild nextSibling

A

B C D

E F G

A

B

C

D

G

F

E

Page 104: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

用左子女用左子女 -- 右兄弟表示实现树的类定义右兄弟表示实现树的类定义template <class Type> class Tree;

template <class Type> class TreeNode {friend class<Type> Tree;private: Type data; TreeNode<Type> *firstChild, *nextSibling;public: TreeNode ( Type value=0, TreeNode<Type> *fc=NULL, TreeNode<Type> *ns=NULL )

Page 105: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

: data (value), firstChild (fc), nextSibling (ns) { }};

template <class Type> class Tree {private: TreeNode<Type> *root, *current; // 根指针及当前指针 void PreOrder ( ostream& out, TreeNode<Type> *p ); // 先根次序遍历并输出以 p 为根的树 int Find ( TreeNode<Type> *p, Type target );

Page 106: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

void RemovesubTree (TreeNode<Type> * p); // 删除以 p 为根的子树 int FindParent ( TreeNode<Type> * t, TreeNode<Type> * p );

// 在树 t 中搜索结点 p 的双亲public: Tree ( ) { root = current = NULL; } int Root ( ); int FirstChild ( ); int NextSibling ( ); int Parent ( );

Page 107: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

int Find ( Type target ); // 树的其它公共操作

……}template <class Type> int Tree <Type> :: Root ( ) {// 让树的根结点成为树的当前结点 if ( root == NULL ) { current = NULL; return 0; } else { current = root; return 1; }}

Page 108: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> int Tree<Type> :: Parent ( ) {// 在树中找当前结点的双亲 , 成为当前结点 TreeNode<Type> * p = current, *t; if ( current == NULL || current == root ) { current = NULL; return 0; } // 空树或根为当前结点 , 返回 0 t = root; int k = FindParent ( t, p ); // 从 t 找 p 双亲 return k;}

Page 109: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type>int Tree<Type> :: FindParent ( TreeNode<Type> *t, TreeNode<Type> *p ) {// 在根为 t 的树中找 p 的双亲 , 成为当前结点 TreeNode<Type> *q = t->firstChild; while ( q != NULL && q != p ) { // 循环根的长子的兄弟链 ,递归在子树中搜索 if (( int i = FindParent (* q,* p) ) != 0 ) return i; q = q->nextSibling; }

Page 110: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

if ( q != NULL && q ==p ) { current = t; return 1; } else return 0; // 未找到双亲 ,当前结点不变}

template <class Type> int Tree <Type> :: FirstChild ( ) {// 在树中找当前结点的长子 ,成为当前结点 if (current != NULL && current->firstChild != NULL ) { current = current→firstChild; return 1; } current = NULL; return 0;}

Page 111: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

template <class Type> int Tree <Type> :: NextSibling ( ) {// 在树中找当前结点的兄弟 ,成为当前结点 if ( current != NULL && current->nextSibling != NULL ) { current = current->nextSibling; return 1; } current = NULL; return 0;}

template <class Type> int Tree<Type> :: Find ( Type target ) { if ( IsEmpty ( ) ) return 0;

Page 112: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

return Find ( root, target );}

template <class Type> int Tree <Type>::Find ( TreeNode <Type> * p, Type target ) {// 在根为 p 的树中找值为 target 的结点 , 找到// 后该结点成为当前结点 , 否则当前结点不变。// 函数返回成功标志: =1, 成功 ; =0, 失败 int result = 0; if ( p->data == target ) { result = 1; current = p; } // 搜索成功 else // 搜索失败

Page 113: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

{ TreeNode<Type> *q = p->firstChild; while ( q != NULL && ! ( result = Find ( q, target ) ) ) q = q->nextSibling; } return result;}

森林与二叉树的转换森林与二叉树的转换

森林与二叉树的对应关系森林与二叉树的对应关系

Page 114: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

T1 T2 T3A F H

T1 T2 T3A

B C D G I J

E K

F

BC

D

E

G

H

I

K J

A

BC

E

DH

I

K J

F

G3 棵树的森林

各棵树的二叉树表示

森林的二叉树表示

Page 115: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

(1) (1) 森林转化成二叉树的规则森林转化成二叉树的规则

若 若 FF 为空为空,即 ,即 nn = 0 = 0 ,则,则 对应的对应的二叉树 二叉树 BB 为为空二叉树空二叉树。。 若 若 FF 不空不空,则,则 对应对应二叉树二叉树 BB 的根的根 root (B) root (B) 是是 FF 中中第一棵树 第一棵树 TT1 1 的根的根 rootroot (T (T11)) ;; 其其左子树左子树为为 B (TB (T1111, T, T1212, …, T, …, T1m1m)) ,,其中,其中, TT1111, T, T1212, …, T, …, T1m 1m 是 是 root (Troot (T11) ) 的子树;的子树; 其其右子树右子树为为 B (TB (T22, T, T33, …, T, …, Tnn)) ,其,其中,中, TT22, T, T33, …, T, …, Tnn 是除 是除 TT1 1 外外其它树构成的森其它树构成的森林林。。

Page 116: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

((2) 2) 二叉树转换为森林的规则二叉树转换为森林的规则

如果 如果 BB 为空,则对应的为空,则对应的森林 森林 FF 也也为空。为空。 如果 如果 BB 非空,则非空,则 FF 中中第一棵树第一棵树 TT11 的根为 的根为 rootroot ;; TT11 的根的子树森林 的根的子树森林 { T{ T1111, T, T1212, …, T, …, T1m 1m }}

是由是由 root root 的的左子树左子树 LBLB 转换而来,转换而来, F F 中除中除了 了 TT1 1 之外之外其余的树组成的森林其余的树组成的森林 { T{ T22, T, T33, …, , …,

TTn n }} 是由是由 rootroot 的的右子树右子树 RBRB 转换而成的森转换而成的森林。林。

Page 117: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

树的二叉树表示树的二叉树表示

树的遍历树的遍历 深度优先遍历深度优先遍历

先根次序遍历先根次序遍历 后根次序遍历后根次序遍历

广度优先遍历广度优先遍历A

B

CE

D

A

B C D

E F G G

F

Page 118: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

(1) (1) 树的先根次序遍历的递归算法树的先根次序遍历的递归算法template <class Type> void Tree<Type> :: PreOrder ( ) {// 以当前指针 current 为根 , 先根次序遍历 if ( IsEmpty ( ) == 0 ) { visit ( ); // 访问根结点 TreeNode<Type> *p = current; current = current ->firstChild; // 第一棵子树 while ( current != NULL ) { PreOrder ( ); // 递归先根遍历子树 current = current ->nextSibling; }

Page 119: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

current = p; // 恢复当前指针 }}

(2) (2) 树的后根次序遍历的递归算法树的后根次序遍历的递归算法template <class Type> void Tree<Type> :: PostOrder ( ) {// 以当前指针 current 为根 , 按后根次序遍历树 if ( IsEmpty ( ) == 0 ) { TreeNode<Type> *p = current; current = current ->firstChild;

Page 120: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

while ( current != NULL ) { PostOrder ( ); current = current->nextSibling; } current = p; visit ( );

// 恢复当前指针 , 最后访问根结点 }}

Page 121: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

(3) (3) 广度优先广度优先 (( 层次次序层次次序 )) 遍历遍历template <class Type> void Tree<Type> :: LevelOrder ( ) {// 按广度优先次序分层遍历树 , 树的根结点是// 当前指针 current 。算法中用到一个队列。 Queue<TreeNode<Type>*> Qu(DefaultSize); TreeNode<Type> *p; if ( current != NULL ) { // 当前指针不空 p = current; // 保存当前指针 Qu.EnQueue ( current ); while ( Qu.IsEmpty ( ) == 0 ) {

Page 122: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

current = Qu.getFront( ); DeQueue ( ); visit ( ); // 队列中取一个并访问之 current = current ->firstChild ;

// 待访问结点的子女结点进队列

while ( current != NULL ) { Qu.EnQueue ( current ); current = current->nextSibling;

} } current = p; // 恢复算法开始的当前指针 }}

Page 123: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

森林的遍历森林的遍历

森林的二叉树表示森林的二叉树表示

(1) (1) 先根次序遍历的规先根次序遍历的规则则::

若森林 若森林 F F 为空为空 ,, 返回;否则返回;否则 访问 访问 F F 的第一的第一棵棵树的根结点;树的根结点; 先根次序遍历第先根次序遍历第一棵树的子树森林;一棵树的子树森林; 先根次序遍历其先根次序遍历其它树组成的森林。它树组成的森林。

A

BC

E

DH

I

K J

F

G

Page 124: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

森林的遍历森林的遍历

森林的二叉树表示森林的二叉树表示

(2) (2) 中根次序遍历的规则:中根次序遍历的规则:

若森林 若森林 F F 为空,为空,返回;否则返回;否则 中根次序遍历第中根次序遍历第一棵树的子树森林;一棵树的子树森林; 访问 访问 F F 的第一的第一棵棵树的根结点;树的根结点; 中根次序序遍历中根次序序遍历其它树组成的森林。其它树组成的森林。

A

BC

E

DH

I

K J

F

G

Page 125: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

森林的遍历森林的遍历

森林的二叉树表示森林的二叉树表示

(3) (3) 后根次序遍历的规则:后根次序遍历的规则:

若森林 若森林 F F 为空,为空,返回;否则返回;否则 后根次序遍历第后根次序遍历第一棵树的子树森林;一棵树的子树森林; 后根次序遍历其后根次序遍历其它树组成的森林;它树组成的森林; 访问 访问 F F 的第一的第一棵棵树的根结点。树的根结点。

A

BC

E

DH

I

K J

F

G

Page 126: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

森林的遍历森林的遍历

森林的二叉树表示森林的二叉树表示

(4) (4) 广度优先遍历广度优先遍历 (( 层次层次序遍历序遍历 ) ) ::

若森林 若森林 F F 为空,为空,返回;否则返回;否则 依次遍历各棵树依次遍历各棵树的根结点;的根结点; 依次遍历各棵树依次遍历各棵树根结点的所有子女;根结点的所有子女; 依次遍历这些子依次遍历这些子女结点的子女结点女结点的子女结点。

A

BC

E

DH

I

K J

F

G

Page 127: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

霍夫曼树 (Huffman Tree)

路径长度 路径长度 (Path Length)(Path Length)

两个结点之间的路径长度 两个结点之间的路径长度 PLPL 是连是连接两结点的路径上的分支数。树的路径长接两结点的路径上的分支数。树的路径长度是各叶结点到根结点的路径长度之和。度是各叶结点到根结点的路径长度之和。

Page 128: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

1 1

2 23 3

4 45 56

6

7

78

8树的路径长度树的路径长度PL = 3*1+2*3 = 9 树的路径长度树的路径长度

PL = 1*1+2*1+3*1+4*1 = 10

n n 个结点的二叉树的路径长度不小于个结点的二叉树的路径长度不小于下述数列前 下述数列前 n n 项的和,即项的和,即

Page 129: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

其路径长度最小者为其路径长度最小者为

1

02 1)(log

n

i

iPL

带权路径长度带权路径长度 ((Weighted Path Length, WPL)Weighted Path Length, WPL)

树的带权路径长度是树的各叶结点所带的权树的带权路径长度是树的各叶结点所带的权值与该结点到根的路径长度的乘积的和。值与该结点到根的路径长度的乘积的和。

1

0

n

iii lwWPL

1

02 1)(log

n

i

iPL

332222110

Page 130: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

具有不同带权路径长度的扩充二叉树具有不同带权路径长度的扩充二叉树

WPL = 2*2+ WPL = 2*1+ WPL = 7*1+WPL = 2*2+ WPL = 2*1+ WPL = 7*1+ 4*2+5*2+ 4*2+5*3+ 5*2+2*3+4*2+5*2+ 4*2+5*3+ 5*2+2*3+ 7*2 = 36 7*3 = 46 4*3 = 35 7*2 = 36 7*3 = 46 4*3 = 35

带权路径长度达到最小带权路径长度达到最小

2

2

24

4

45

5

5

7

7

7

Page 131: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

霍夫曼树霍夫曼树 带权路径长度达到最小的扩充二叉树即为带权路径长度达到最小的扩充二叉树即为霍夫曼树。霍夫曼树。

在霍夫曼树中,权值大的结点离根最近。在霍夫曼树中,权值大的结点离根最近。

霍夫曼算法霍夫曼算法 (1)(1) 由给定的由给定的 nn 个权值 个权值 {{ww00, , ww11, , ww22, , ……, , wwnn--

11}} ,构造具有 ,构造具有 nn 棵扩充二叉树的森林 棵扩充二叉树的森林 FF = { = {

TT00, , TT11, , TT22, , ……, , TTnn-1-1 }} ,其中每棵扩充二叉树 ,其中每棵扩充二叉树 TTi i

只有一 个带权值 只有一 个带权值 wwi i 的根结点的根结点 , , 其左、右子其左、右子树均为空。树均为空。

Page 132: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

(2)(2) 重复以下步骤重复以下步骤 , , 直到 直到 FF 中仅剩下一中仅剩下一棵树为止:棵树为止: ① ① 在 在 FF 中选取两棵中选取两棵根结点的权值最根结点的权值最小小的扩充二叉树的扩充二叉树 , , 做为左、右子树做为左、右子树构造一构造一棵新的二叉树。置新的二叉树的棵新的二叉树。置新的二叉树的根结点的权根结点的权值为其左、右子树上根结点的权值之和值为其左、右子树上根结点的权值之和。。 ② ② 在 在 FF 中删去这两棵二叉树。中删去这两棵二叉树。 ③ ③ 把新的二叉树加入 把新的二叉树加入 FF 。。

霍夫曼树的构造过程霍夫曼树的构造过程

Page 133: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

F : {7} {5} {2} {4} F : {7} {5} {6}

7 5 2 4

初始合并 {2} {4}

F : {7} {11}

7 5

2 4

7 5

2 4

6

6

11

合并 {5} {6}

F : {18}

5合并 {5} {6}

2

7

4

6

11

18

Page 134: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

扩充二叉树的类定义扩充二叉树的类定义const int DefaultSize = 20; template <class Type> class ExtBinTree;

template <class Type> class Element {friend class ExtBinTree;private: Type data; Element<Type> * leftChild, * rightChild;};

template <class Type> class ExtBinTree {

Page 135: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

public: ExtBinTree (ExtBinTree<Type>& bt1, ExtBinTree<Type> & bt2 ) { root->leftChild = bt1.root; root->rightChild = bt2.root; root->data.key = bt1.root->data.key + bt2.root->data.key; } protected: Element<Type> *root; // 二叉树的根}

Page 136: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

建立霍夫曼树的算法建立霍夫曼树的算法template <class Type> void HuffmanTree (Type *fr, int n, ExtBinTree <Type> & newtree ) { ExtBinTree <Type> & first, & second; ExtBinTree <Type> Node[DafualtSize]; MinHeap < ExtBinTree <Type> > hp; if ( n > DefaultSize ) { cerr << “ 大小 n ” << n << “ 超出了数组 边界 ” << endl; return; }

Page 137: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

for ( int i = 0; i < n; i++ ) { Node[i].root->data.key = fr[i]; Node[i].root->leftChild = Node[i].root ->rightChild = NULL; } hp.MinHeap ( Node, n ); // 建立存储扩充二叉树森林的最小堆 for ( int i = 0; i < n-1; i++ ) { // 做 n-1 趟 , 逐步形成霍夫曼树 hp.RemoveMin ( first ); // 选择最小 hp.RemoveMin ( second ); // 选择次小

Page 138: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

newtree = new ExtBinTree<Type> (first, second); // 合并 hp.Insert ( newtree );

// 重新插入到最小堆中 }}

霍夫曼编码霍夫曼编码

主要用途是实现数据压缩。主要用途是实现数据压缩。

Page 139: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

设给出一段报文:设给出一段报文: CAST CAST SAT AT A TASACAST CAST SAT AT A TASA 字符集合是 字符集合是 { C, A, S, T }{ C, A, S, T } ,各个字符,各个字符出现的频度出现的频度 (( 次数次数 )) 是 是 WW == { 2, 7, 4, 5 }{ 2, 7, 4, 5 } 。。 若给每个字符以等长编码若给每个字符以等长编码 AA : 00 T : 10 C : 01 S : 11 : 00 T : 10 C : 01 S : 11

则总编码长度为则总编码长度为 ( 2+7+4+5 ) * 2 = 36( 2+7+4+5 ) * 2 = 36.. 若按各个字符出现的概率不同而给予不若按各个字符出现的概率不同而给予不等长编码,可以减少总编码长度。等长编码,可以减少总编码长度。 各字符出现概率为各字符出现概率为 { 2/18, 7/18, 4/18, 5/1{ 2/18, 7/18, 4/18, 5/188 }},, 化整为化整为 { 2, 7, 4, 5 }{ 2, 7, 4, 5 } 。。

Page 140: 树和森林的概念  二叉树  二叉树遍历 二叉树的计数 线索化二叉树 堆 树与森林 霍夫曼树

以它们为各叶结点上的权值以它们为各叶结点上的权值 , , 建立霍建立霍夫曼树。夫曼树。左分支左分支赋 赋 00 ,,右分支右分支赋赋 11 ,得霍,得霍夫曼编码夫曼编码 (( 变长编码变长编码 )) 。 。 AA : 0 T : 10 C : 110 S : 111 : 0 T : 10 C : 110 S : 111它的总编码长度它的总编码长度:: 7*1+5*2+( 2+4 )*3 = 357*1+5*2+( 2+4 )*3 = 35 。。比等长编码的长度要短。比等长编码的长度要短。 总编码长度正好等于霍夫总编码长度正好等于霍夫曼树的带权路径长度曼树的带权路径长度 WPLWPL 。。 霍夫曼编码是一种无前缀霍夫曼编码是一种无前缀编码。解码时不会混淆。编码。解码时不会混淆。

霍夫曼编码树2 4

5

70

0

0

1

1

1