树和 二叉树 二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数 堆 ...

162
树树 树树树 树树 树树树 树树树树树 树树树树树 树树树树树 树树树树树 树树树树树 树树树树树 树树树树树树 树树树树树树 树树树树 树树树树 树树树树树树树树 树树树树树树树树

Upload: rio

Post on 17-Jan-2016

225 views

Category:

Documents


0 download

DESCRIPTION

第六章 树和森林. 树和 二叉树 二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数 堆 树与森林 霍夫曼树及其应用. 一、树和二叉树. 树 tree 的定义 (1) 无结点的树 空树 (2) 非空树 仅有一个 根 结点 其余结点分为若干 互不相交的 子树. A. ········································ 根. 3 度. D. B. C. H. I. J. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

树和 二叉树 树和 二叉树 二叉树遍历二叉树遍历 线索二叉树线索二叉树 二叉搜索树二叉搜索树 二叉树的计数二叉树的计数 堆堆 树与森林树与森林 霍夫曼树及其应用 霍夫曼树及其应用

Page 2: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

一、树和二叉树

树 tree 的定义 (1) 无结点的树 空树 (2) 非空树 仅有一个根结点 其余结点分为若干 互不相交的子树

Page 3: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B C D

E F G H I J

K L M

········································ 根

3 度

·············叶

0 度

2 个 3 度点

A,D

2 个 2 度点

B,E

2 个 1 度点

C,H

7 个叶

F,G,I,J,K,L,M

Page 4: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B C D

E F G H I J

K L M

············································· 第一层 ······························第二层 ··············· 第三层

····································第四层

树的深度为4

Page 5: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B C

D E F G

H I J

2. 二叉树 每个结点至多有两棵子树 ,1 度或 2度

Page 6: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

深度为 k ,结点至多 2k-1 个第 i 层结点至多 2i-1 个

设有 n2 个 2 度点

则有 n2+1 个叶片A

B C

D E F G

H I J

Page 7: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B C

D E F G

H I J

满二叉树

K L M N O

深度为 k, 结点数 2k-1, 每层结点数都最大

Page 8: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

满二叉树 满二叉树去掉最下层最右边若干结点

满二叉树也是完全二叉树

完全二叉树

A

B C

D E F G

H I J K L M N O

Page 9: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B C

D E F G

H I J

完全二叉树

K L M

n 个结点 , 深度 k=[log2n] +1

n 个结点 , 深度为k:

2k-1-1 < n≤2k-1

2k-1 ≤ n < 2k

k-1 ≤ log2n < k

k-1 = [log2n]

k = [log2n] +11 个结点深度为 1

2-3 个结点深度为 2

4-7 个结点深度为 3

8-15 个结点深度为 4

Page 10: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

1

2 3

4 5 6 7

8 9 10

完全二叉树

11 12 13

结点 i 的左子结点是 2i

右子结点是 2i+1

结点 i 的父结点是 [i/2]

Page 11: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉树的存储

1. 完全二叉树的顺序存储 #define MaxTreeSize 100

typedef TElemType SqBiTree[MaxTreeSize];

SqBiTree bt;

1 2 3 4 5 6 7 8 9 10 11 12

Page 12: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

0

1 2

3 4 5 6

7 8 9

完全二叉树

10 11 12

结点 i 的左子结点是 2i+1

右子结点是 2i+2

结点 i 的父结点是 [(i-1)/2]

1 2 3 4 5 6 7 8 9 10 11 12

Page 13: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉树的链式存储

树结点 左指针 数据 右指针

lchild data rchild

Page 14: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B ^ C

^ D ^ E ^ F ^

^ G ^ ^ H ^

A

B C

D E F

G H

Page 15: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉树结点类的定义

#ifndef TREENODE_CLASS

#define TREENODE_CLASS

#ifndef NULL

const int NULL = 0;

#endif // NULL

Page 16: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>class BinSTree;template <class T>class TreeNode{ protected: TreeNode<T> *left , *right; public: T data; TreeNode (const T& item, TreeNode<T> *lptr = NULL, TreeNode<T> *rptr = NULL); virtual ~TreeNode(void); TreeNode<T>* Left(void) const; TreeNode<T>* Right(void) const; void GetLeft(TreeNode<T>*lptr); void GetRight(TreeNode<T>*rptr); friend class BinSTree<T>;};

Page 17: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// the pointer NULL assigns an empty tree

template <class T>

TreeNode<T>::TreeNode (const T& item, TreeNode<T> *lptr,

TreeNode<T> *rptr): data(item), left(lptr), right(rptr)

{}

Page 18: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// method Left allows the user// to reference the left child

template <class T>

TreeNode<T>* TreeNode<T>::Left(void) const

{

// return the private member value left

return left;

}

Page 19: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// method Left allows the user //to reference the right child

template <class T>TreeNode<T>* TreeNode<T>::Right(void) const

{

// return the private member value right

return right;

}

Page 20: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// does nothing. //exists so nodes derived from it will be

// destroyed properly by delete.

//used in Chapter 13 for AVL trees

template <class T>

TreeNode<T>::~TreeNode(void)

{}

#endif

Page 21: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

#ifndef TREE_LIBRARY_FUNCTIONS

#define TREE_LIBRARY_FUNCTIONS

#include <iostream.h>

#include <stdlib.h>

#include "treenode.h"

#ifndef NULL

const int NULL = 0;

#endif // NULL

二叉树结点的操作函数 //treelib.h

Page 22: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>TreeNode<T> *GetTreeNode(T item, TreeNode<T> *lptr= NULL, TreeNode<T> *rptr = NULL)

{ TreeNode<T> *p; p = new TreeNode<T> (item, lptr, rptr); if (p == NULL) { cerr << "Memory allocation failure!\n"; exit(1); }

return p;}

建立一个结点

Page 23: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

撤销一个结点

template <class T>

void FreeTreeNode(TreeNode<T> *p)

{

delete p;

}

Page 24: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

void MakeCharTree(TreeNode<char>* &root, int n){ TreeNode<char> *a, *b, *c, *d, *e, *f, *g, *h, *i; switch(n) { case 0: d = GetTreeNode('D'); e = GetTreeNode('E'); b = GetTreeNode('B',(TreeNode<char> *)NULL, d); c = GetTreeNode('C',e, (TreeNode<char> *)NULL); a = GetTreeNode('A',b, c); root = a; break;

创建三棵树

A

B C

D E

Page 25: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

case 1:

g = GetTreeNode('G');

h = GetTreeNode('H');

i = GetTreeNode('I');

d = GetTreeNode('D');

e = GetTreeNode('E',g, (TreeNode<char> *)NULL);

f = GetTreeNode('F',h, i);

b = GetTreeNode('B',d, e);

c = GetTreeNode('C',(TreeNode<char> *)NULL, f);

a = GetTreeNode('A',b, c);

root = a;

break;

A

B C

D E F

G IH

Page 26: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

case 2:

g = GetTreeNode('G');

h = GetTreeNode('H');

i = GetTreeNode('I');

d = GetTreeNode('D',(TreeNode<char> *)NULL, g);

e = GetTreeNode('E',h, i);

f = GetTreeNode('F');

b = GetTreeNode('B',d, (TreeNode<char> *)NULL);

c = GetTreeNode('C',e, f);

a = GetTreeNode('A',b, c);

root = a;

break;}

}

A

B C

D E F

G IH

Page 27: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

为代数式 a+b*(c-d)-e/f 建一棵树使 a,b,c,d,e,f 为叶,两个数之间的运算符为根 .

+ /

a * e f

b —

c d

a=GetTreeNode(‘a’,(TreeNode<char>)NULL,

(TreeNode<char>)NULL);

p1=GetTreeNode(‘-’,c,d);

p2=GetTreeNode(‘*’,b,p1);

p3=GetTreeNode(‘+’,a,p2);p4=GetTreeNode(‘/’,e,f);

p5=GetTreeNode(‘-’,p3,p4);

root=p5;

Page 28: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二、二叉树的遍历先序遍历 PreOrderTraverse 先访问根结点 再访问左子树 后访问右子树中序遍历 InOrderTraverse 先访问左子树 再访问根结点 后访问右子树 后序遍历 PostOrderTraverse 先访问左子树 再访问右子树 后访问根结点层次遍历( Level Traverse )

Page 29: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

先序遍历template <class T>void Preorder (TreeNode<T> *t, void visit(T& item)){ // the recursive scan terminates on a empty subtree if (t != NULL) { visit(t->data); // visit the node Preorder (t->Left( ), visit); // descend left Preorder (t->Right( ), visit); // descend right }}

A

B C

D E F

G IH

Preorder(root,print);

ABDEGCFHI

void print(char& itm){cout<<itm;}

Page 30: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

中序遍历template <class T>void Inorder (TreeNode<T> *t, void visit(T& item)){ // the recursive scan terminates on a empty subtree if (t != NULL) { Inorder(t->Left( ), visit); // descend left visit(t->data); // visit the node Inorder (t->Right( ), visit); // descend right }}

A

B C

D E F

G IH

DBGEACHFI

Page 31: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

后序遍历template <class T>void Postorder (TreeNode<T> *t, void visit(T& item)){ // the recursive scan terminates on a empty subtree if (t != NULL) { Postorder (t->Left( ), visit); // descend left Postorder (t->Right( ), visit); // descend right visit(t->data); // visit the node }}

A

B C

D E F

G IH

DGEBHIFCA

Page 32: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void CountLeaf (TreeNode<T> *t, int& count){ if (t != NULL) {CountLeaf(t->Left( ), count); CountLeaf(t->Right( ), count);

if (t->Left( ) == NULL && t->Right( ) == NULL)

count++; }}

二叉树的运算

Page 33: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

int Depth (TreeNode<T> *t){ int depthLeft, depthRight, depthval; if (t == NULL) depthval = 0; else { depthLeft= Depth(t->Left( )); depthRight= Depth(t->Right( )); depthval = 1 + (depthLeft> depthRight?depthLeft:depthRight);

} return depthval;}

Page 34: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

TreeNode<T> *CopyTree(TreeNode<T> *t)

{ TreeNode<T> *newlptr, *newrptr, *newnode;

if (t == NULL) return NULL;

if (t->Left( ) = NULL) newlptr = NULL;

else newlptr = CopyTree(t->Left( ));

if (t->Right( ) = NULL) newrptr = NULL;

else newrptr = CopyTree(t->Right());

newnode = GetTreeNode(t->data, newlptr, newrptr);

return newnode;

}

Page 35: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

void DeleteTree(TreeNode<T> *t){ if (t != NULL) { DeleteTree(t->Left()); DeleteTree(t->Right()); FreeTreeNode(t); }}

Page 36: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

输入一棵树按先序输入一棵树键盘输入:AB#D##CE###template<class T>void CreateTree(TreeNode<T>*&t ){TreeNode<T>*l,*r; T ch; cin>>ch; if(ch==‘#’)t=NULL; else{t=GetTreeNode(ch); CreateTree(l); t->GetLeft(l);

CreateTree(r);t->GetRight(r);}}

A

B C

D E

Page 37: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void ClearTree(TreeNode<T> * &t)

{

DeleteTree(t);

t = NULL; // root now NULL

}

#endif // TREE_LIBRARY_FUNCTIONS

Page 38: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

层次遍历——广度优先法遍历树ABCDEFGHItemplate <class T>

void LevelScan(TreeNode<T>*t,void visit(T& item))

{Queue<TreeNode<T>*> Q;

TreeNode<T> * p;

Q.Qinsert(t);

while(!Q.Qempty( ))

{ p=Q.Qdelete( ); visit(p->data);

if(p->Left( )!=NULL)Q.Qinsert(p->Left( ));

if(p->Right( )!=NULL)Q.Qinsert(p->Right( )); }

}

A

B C

D E F

G IH

Page 39: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

测试#include <iostream.h>#include "treenode.h"#include "treelib.h"void print(char& item) {cout<<item;}void main(void){ TreeNode<char> *root; MakeCharTree(root, 2); int leafCount = 0; CountLeaf(root, leafCount); cout << "Number of leaf nodes is " << leafCount << en

dl; cout << "The depth of the tree is " << Depth(root) << endl; CreateTree(root); Postorder(root,print); }

Page 40: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

三、线索二叉树

带线索的二叉树,即每个结点都带有指向其前驱和后继的指针的二叉树

lchild ltag data rtag rchild

ltag=0 lchild 指向左子结点 =1 前驱rtag=0 rchild 指向右子结点 =1 后继

优点:遍历查找方便快捷,从任何结点可

以向前向后搜索

用途:需要反复遍历和查找的二叉树

Page 41: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

中序线索化树

lchild ltag data rtag rchild

A

B C

D E

0 A 0

1 B 0 0 C 1

1 D 1 1 E 1

1 1

Page 42: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

线索二叉树的结点类#include<iostream.h>#include<stdio.h>

typedef enum{

Link, Thread //Link==0; Thread==1;}PointerTag;

Page 43: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>class ThreadNode{

ThreadNode<T> *left, *right; //pointerPointerTag LTag, RTag;

// tags for link or thread public:

T data;ThreadNode(const T item):data(item),

left(NULL),right(NULL),LTag(0),RTag(0){ }

}

Page 44: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void InOrderThreading(ThreadNode<T>*&thrt, ThreadNode<T>* tree){

ThreadNode<T> *pre;thrt=new ThreadNode<T>;if(!thrt) { cerr<<"Memory overflow!"; exit(1); }thrt->LTag=Link;thrt->RTag=Thread;thrt->right=thrt; //right pointer point to its self

Page 45: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

if(tree==NULL) thrt->left=thrt; //if "tree" is empty, //left pointer point to its self either

else { thrt->left=tree; pre=thrt; InThreading(tree); pre->RTag=Thread; pre->right=thrt; thrt->right=pre; }

}

Page 46: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void InThreading(ThreadNode<T> *p){ if(p) { InThreading(p->left); if(p->left==NULL)

{ p->LTag=Thread; p->left=pre; }

if(pre->right==NULL) { pre->RTag=Thread;

pre->right=p; } pre=p; InThreading(p->rchild); }

}

Page 47: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void InOrderTravers(ThreadNode<T>*t , void visit(T item)){ ThreadNode *p; p = t->left; while(p!=t) { while(p->LTag==Link)

p=p->left; visit(p->data); while(p->RTag==Thread&&p->right!=t) { p=p->right; visit(p->data); } p = p->right;

}}

Page 48: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

四、二叉搜索树 (二叉排序树) 其左非空子树上所有结点的值都小于根结点的值 其右非空子树上所有结点的值都大于根结点的值 左右子树也是二叉搜索树

45

12 57

8 20 60

3 11 59

50

二叉搜索树的作用:排序,检索

Page 49: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉搜索树#ifndef BINARY_SEARCH_TREE_CLASS

#define BINARY_SEARCH_TREE_CLASS

#include <iostream.h>

#include <stdlib.h>

#ifndef NULL

const int NULL = 0;

#endif // NULL

#include "treenode.h"

Page 50: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T> class BinSTree

{ TreeNode<T> *root, *current; int size; TreeNode<T> *GetTreeNode(const T& item, TreeNode<T> *lptr,TreeNode<T> *rptr);

void FreeTreeNode(TreeNode<T> *p); TreeNode<T> *CopyTree(TreeNode<T> *t); void DeleteTree(TreeNode<T> *t); TreeNode<T> *FindNode(const T& item, TreeNode<T>* & parent) const;

Page 51: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

public:

BinSTree(void);

BinSTree(const BinSTree<T>& tree);

~BinSTree(void); BinSTree<T>& operator= (const BinSTree<T>& rhs);

int Find(T& item);

void Insert(const T& item);

void Delete(const T& item);

void ClearList(void);

int ListEmpty(void) const;

int ListSize(void) const;

void Update(const T& item);

TreeNode<T> *GetRoot(void) const;

};

Page 52: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

TreeNode<T> *BinSTree<T>::GetTreeNode(const T& item,TreeNode<T> *lptr,TreeNode<T> *rptr)

{ TreeNode<T> *p; p = new TreeNode<T> (item, lptr, rptr); if (p == NULL) { cerr << "Memory allocation failure!\n"; exit(1); } return p;}

Page 53: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// delete the storage occupied by a tree node

template <class T>void BinSTree<T>::FreeTreeNode(TreeNode<T> *p)

{

delete p;

}

Page 54: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>TreeNode<T> *BinSTree<T>::CopyTree(TreeNode<T> *t)

{ TreeNode<T> *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;}

Page 55: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void BinSTree<T>::DeleteTree(TreeNode<T> *t)

{

if (t != NULL)

{

DeleteTree(t->left);

DeleteTree(t->right);

FreeTreeNode(t);

}

}

Page 56: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>TreeNode<T> *BinSTree<T>::FindNode(const T& item,

TreeNode<T>* & parent) const

{ TreeNode<T> *t = root;

parent = NULL;

while(t != NULL)

{ if (item == t->data) break;

else { parent = t;

if (item < t->data) t = t->left;

else t = t->right; }

}

return t;

}

Page 57: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

BinSTree<T>::BinSTree(void): root(NULL),current(NULL),size(0)

{ }

Page 58: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

BinSTree<T>::BinSTree(const BinSTree<T>& tree)

{

// copy tree to the current object. assign current and size

root = CopyTree(tree.root);

current = root;

size = tree.size;

}

Page 59: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

BinSTree<T>::~BinSTree(void)

{

// just call ClearList

ClearList( );

}

Page 60: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>BinSTree<T>& BinSTree<T>::operator= (const

BinSTree<T>& rhs)

{ if (this == &rhs) return *this;

ClearList( );

root = CopyTree(rhs.root);

current = root;

size = rhs.size;

return *this;

}

Page 61: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>int BinSTree<T>::Find(T& item){ TreeNode<T> *parent; current = FindNode (item, parent); if (current != NULL) { item = current->data; return 1; } else return 0;}

Page 62: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void BinSTree<T>::Insert(const T& item){TreeNode<T> *t = root, *parent = NULL, *newNode;

while(t != NULL) { parent = t; if (item < t->data) t = t->left; else t = t->right; } newNode = GetTreeNode(item,NULL,NULL); if (parent == NULL) root = newNode; else if (item < parent->data) parent->left = newNode; else parent->right = newNode; current = newNode; size++;}

Page 63: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉搜索树的插入过程

45 12 8 57 60 20 11 59 50 3

45

12 57

8 20 60

3 11 59

50

57 20 8 45 60 59 3 12 50 11

57

20 60

8 45

11

3 12 50

59

Page 64: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉搜索树的删除过程先调用函数 FindNode 查到要删除的结点 D(DNodePtr),D 的父结点 P(PNodePtr), 我们寻找替换结点 R(RNodePtr).

45

12 57

15

20 60

59

50

1. D 是叶令 R=NULL; P->left=R; 即可。

D

P

R

Page 65: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉搜索树的删除过程先调用函数 FindNode 查到要删除的结点 D(DNodePtr),D 的父结点 P(PNodePtr), 我们寻找替换结点 R(RNodePtr).

45

12 57

15

20 60

59

50

1. D 是叶令 R=NULL; P->left=R; 即可。

D

P

R

2. D 只有左子树令 R=D->left;

15

15R

15R

P->right=R;

Page 66: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉搜索树的删除过程先调用函数 FindNode 查到要删除的结点 D(DNodePtr),D 的父结点 P(PNodePtr), 我们寻找替换结点 R(RNodePtr).

45

12 57

15

20 60

59

50

1. D 是叶令 R=NULL; P->left=R; 即可。

D

P

2. D 只有左子树 , D->right=NULL

令 R=D->left;

P->right=R;

3. D 只有右子树 , D->left=NULL令 R=D->right;

P->left=R;

R15

20R

15

20R

Page 67: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

57

20 60

8 45

11

3 12 50

59

二叉搜索树的删除过程4.D 的左右子树都不空

D

P

R R

Q=D;R=D->left;

while(R->right){Q=R,

R=R->right:}

R选择D的左子树中最大结点(或右子树中最小结点)

有两种情况:

1. Q==D

R->right=D->right;

2. Q!=D

Q->right=R->left;

再用 R 代替 D

R

11

12R

Q

Q

R

Page 68: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

57

20 60

8 45

3 11 50

59

二叉搜索树的删除过程

D

P

R

Q=D;R=D->left;

while(R->right){Q=R, R=R->right:}

2. Q!=D Q->right=R->left;

再用 R 代替 D :

Q

有两种情况: 1. Q==D R->right=D->right;

R->right=D->right;

R->left=D->left;

12

R

R

R 12

最后让 P 指向 R

如果 P==NULL

则 R 是根,否则

D 在 P 左 R 在左

Page 69: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void BinSTree<T>::Delete(const T& item){TreeNode<T> *DNodePtr, *PNodePtr, *RNodePtr;

if ((DNodePtr = FindNode (item, PNodePtr)) == NULL) return; if (DNodePtr->right == NULL) RNodePtr = DNodePtr->left; else if (DNodePtr->left == NULL) RNodePtr = DNodePtr->right; else {TreeNode<T> *PofRNodePtr = DNodePtr; RNodePtr = DNodePtr->left; while(RNodePtr->right != NULL) { PofRNodePtr = RNodePtr; RNodePtr = RNodePtr->right; }

Page 70: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

if (PofRNodePtr == DNodePtr)

RNodePtr->right = DNodePtr->right;

else{PofRNodePtr->right = RNodePtr->left;

RNodePtr->left = DNodePtr->left;

RNodePtr->right = DNodePtr->right;} }

if (PNodePtr == NULL) root = RNodePtr;

else if (DNodePtr->data < PNodePtr->data)

PNodePtr->left = RNodePtr;

else PNodePtr->right = RNodePtr;

FreeTreeNode(DNodePtr); size--;

}

Page 71: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void BinSTree<T>::ClearList(void){ DeleteTree(root); root = NULL; current = NULL; size = 0;}

Page 72: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

int BinSTree<T>::ListEmpty(void) const

{

return root == NULL;

}

Page 73: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

int BinSTree<T>::ListSize(void) const

{

return size;

}

Page 74: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// assign node value to item ;// otherwise, insert item in treetemplate <class T>void BinSTree<T>::Update(const T& item){ if (current != NULL && current->data == item) current->data = item; else Insert(item);}

Page 75: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>TreeNode<T> *BinSTree<T>::GetRoot(void) const

{

return root;

}

#endif

// BINARY_SEARCH_TREE_CLASS

Page 76: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

测试练习

1 。用二叉搜索树为一个数列排序, 分别用前序和后序再输出。

2 。键盘输入若干个字符串,排序后输出。

3 。在输入的字符串中删除两个串,再输出。

Page 77: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

练习

一 . 画出以下输入所对应二叉搜索树: 1 ) R,O,T,A,R,Y,C,L,U,B

2 ) 60 , 25 , 7 , 99 , 15 , 3 , 10

38 , 59 , 62 , 34

二 . 分别给出这两棵树的前序,中序,后序遍历输出

Page 78: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

练习

一棵二叉树,先序序列为 EBADCFHGIKJ

中序序列为 ABCDEFGHIJK

画出这棵树

Page 79: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

五、树的计数有 n 个结点的不同形态的二叉树有多少?不考虑结点的数据的异同树 T 和 T’ 相似: T 和 T’ 都是空树或两者都不是空树且 左右子树分别相似。设 n 个结点互不相似的二叉树的数目为 bn

则 b0=1, b1=1, b2=2 。

Page 80: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

b1=1

b2=2

b3=5=b2*b0+b1*b1+b0*b2

Page 81: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

b0=1 , b1=b0*b0=1,b2=b0*b1+b1*b0=2,b3=b0*b2+b1*b1+b2*b0=5, b4=b0*b3+b1*b2+b2*b1+b3*b0

=1*5+1*2+2*1+5*1=14,b5= b0*b4+b1*b3+b2*b2+b3*b1+b4b0

=1*14+1*5+2*2+5*1+14*1=42 ,递推关系

bn=Σbibn-i-1i=0

n-1

Page 82: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

b0, b1,b2,······,bn,······

定义:

B(x)=b0+ b1x+b2x2+······+bnxn+······

则B2(x)=b0b0+ (b0 b1+b1b0)x+(b0b2+b1b1+b2b0)x2+······

= b1+b2x+b3x2+······

xB2(x)= b1x+b2x2+b3x3+······

xB2(x) =B(x)-1

Page 83: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

xB2(x) =B(x)-1

xB2(x) -B(x)+1=0

解方程

B(x)=(1±√1-4x )/2x

由 limx→0B(x)=b0=1

B(x)=(1 -√ 1-4x )/2x

Page 84: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

B(x)=(1 -√ 1-4x )/2x设 f(x)=√1-4x =(1-4x)1/2

f(x) 的戴劳展开式 f(x) =a0+a1x+a2x2+······+anxn+······

an=f(n)(0)/n!

a0=1 a1=(1/2)(-4)

a2=(1/2)(1/2 - 1)(-4)2/2!

······

an=(1/2)(1/2 - 1)···(1/2 - (n - 1))(-4)n/n!

Page 85: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

设 f(x)=√1-4x =(1-4x)1/2

f(x) =a0+a1x+a2x2+······+anxn+······ an=(1/2)(1/2 - 1)···(1/2 - (n - 1))(-4)n/n!, n≥1

记 C1/2k= (1/2)(1/2 - 1)···(1/2 - (k - 1))/k!

an= C1/2n(-4)n=(-1)n22nC1/2

n

f(x) =1 - 4C1/21x+ 24C1/2

2x2+···

+ (-1)n22nC1/2nxn+······

B(x)=(1 -√ 1-4x )/2x=2 C1/2

1 - 23C1/22x +···+(-1)n-122n-1C1/2

nxn-1+···

Page 86: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

B(x)=b0+ b1x+b2x2+······+bnxn+······

比较系数得到

b0=1, b1= - 23C1/22,

bn-1=(-1)n-122n-1C1/2n,

bn=(-1)n22n+1C1/2n+1.

B(x) =2 C1/21 - 23C1/2

2x +···+(-1)n-122n-1C1/2nxn-1+···

Page 87: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

bn=(-1)n22n+1C1/2n+1

b0=1,

b1=(-1)23C1/22=(-1)23(1/2)(1/2 - 1)/2!=1

b2=(-1)225C1/23=25(1/2)(1/2 - 1)(1/2 - 2)/3!

=25(1/2)(-1/2)(-3/2)/3!=22*1*3/3!=2

b3=(-1)327C1/24=27(1/2)(1/2)(3/2)(5/2)/4!=5

=23*1*3*5/4!=(1/4)(23*1*3*5*1*2*3)/(3!*3!)

=(1/4)(1*3*5*2*4*6)/(3!*3!)

=(1/4)6!/(3!*3!)=1/4*C63

Page 88: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

bn=(-1)n22n+1C1/2n+1

=(-1)n22n+1 21(2

1- 1)(2

1- 2) ···(2

1- n)

(n+1)!

=2n1*3*5*···*(2n-1)

(n+1)n!*n!*n!

=n+11

n!n!(2n)!

= n+11 C2n

n

Page 89: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

n+11 C2n

nbn=

b3= 41 C6

3 = 46!

*3!*3!=5

b4= 51 C8

4 = 58!

*4!*4!=14

b5= 61 C10

5 = 610!

*5!*5!=42

b6= 71 C12

6 = 712!

*6!*6!

=8*9*10*11*121*2*3*4*5*6

=132

Page 90: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

n+11 C2n

nbn=

C2nn - C2n

n-1=(2n)!n!*n!

-(2n)!

(n-1)!*(n+1)!

=(2n)!

n!*(n+1)!C2n

nC2nnC2nnC2nnC2nnC2nnC2nn

n+11 C2n

n =

n+11 C2n

nbn= =C2nn - C2n

n-1

Page 91: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

树的计数

树可以转换成唯一的二叉树

即不同的树转换成不同的二叉树

不同的二叉树也可以转换成不同的树

只要根结点没有左子树

因此 n 个结点的树与 n-1 个结点的二叉树

的数目相同

tn=bn-1

Page 92: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

t1=1

t2=1

t3=2

Page 93: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

t4=b3

Page 94: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

n 个元素进栈,有多少不同的出栈方法

记 n 个元素进栈有 bn 种出栈法设 n 个元素为 a1,a2,······,an

ak+1

a3

a2

a1

设 a1 在第 k+1个元素出栈

ai1ai2···aika1aj1aj2···ajn-k-1

a1 前 k 个元素有 bk 种排法 a1 后 n-k-1个元素有 bn-k-1 种排法,共有 bk*bn-k-1 种排法

得到递推关系

bn=Σbkbn-k-1k=0

n-1

Page 95: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

n 个元素进栈,有多少不同的出栈方法

记 n 个元素进栈有 bn 种出栈法

bn=Σbkbn-k-1k=0

n-1

n+11 C2n

nbn= =C2nn - C2n

n-1

Page 96: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

六、树和森林

若干棵树组成森林

Page 97: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

树的存储结构

双亲表示法 孩子表示法 孩子兄弟表示法

Page 98: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

双亲表示法

用数组存储树的结点每个结点中附设一个字段指示其父结点的位置

A

B C D

E F G

H I J

0

1

2

3

4

5

6

7

8

9

A -1

B 0

C 0

D 0

E 1

F 1

G 3

H 6

I 6

J 6

Page 99: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

双亲表示法结点定义#define MAX_TREE_SIZE 100template <class T>class PNode { T data; int Parent; public: PNode(T item, int pr); T GetData( ); int GetParent( ); }}

Page 100: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

树的双亲表示法定义

template <class T>class PTree{ PNode<T> nodes[MAX_TREE_SIZE]; int n; //number of nodes public: PTree( int m=0); PNode<T> operator[ ](int i); int PTreeInsert(T item, int pr); T PTreeDelete(int i); int PTreeSize( ); }

Page 101: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

孩子表示法

1. k 叉树结点表示 ( 固定长结点)

A

B C D

E F G

H I

data child1 child2 ······ childk

A

B C D

E F G

H I

Page 102: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

孩子表示法

1. k 叉树结点表示 ( 固定长结点)

A

B C D

E F G

H I J

2. 变长结点表示data degree child1 child2 ······ childk

A 3

B 2 C 0 D 1

E 0 F 0 G 2

H 0 I 0

Page 103: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

孩子表示法

1. k 叉树结点表示 ( 固定长结点)

A

B C D

E F G

H I

2. 变长结点表示3. 孩子链表表示

0

1

2

3

4

5

6

7

8

A

B

C ^

D

E ^

F ^

G

H ^

I ^

1 2 3

7 8

4 5

6

Page 104: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

孩子链表表示结点类的定义struct ChildNode //child node{ int child; ChildNode *next; };

template <class T>struct CTNode{ T data; ChildNode *firstChild;};

template <class T>class CTree{ CTNode<T> node[MAX_TREE_SIZ

E]; int n; ……};

Page 105: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

带双亲的

A

B C D

E F G

H I

0

1

2

3

4

5

6

7

8

A

B

C ^

D

E ^

F ^

G

H ^

I ^

1 2 3

7 8

4 5

6

0

1

2

3

4

5

6

7

8

-1 A

0 B

0 C ^

0 D

1 E ^

1 F ^

3 G

5 H ^

5 I ^

孩子表示法

Page 106: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

孩子兄弟表示法(二叉树表示法)

结点 : 数据 孩子 兄弟

A

B C D

E F G

H I

data firstchild nextsibling

A

B C D

E F G

H I

Page 107: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

孩子兄弟表示法(二叉树表示法)

结点 : 数据 孩子 兄弟

A

B C D

E F G

H I

data firstchild nextsiblingA

B

C

D

E

F

G

H

I

Page 108: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// 孩子兄弟表示结点类的定义template <class T> CSTree;template <class T>class CSNode{ public: T data; CSNode<T> *firstChild, *nextSibling; CSNode( T item=0, CSNode<T>*fc=NULL, CSNode<T>*ns=NULL);

friend class <T> CSTree; };

Page 109: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用
Page 110: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// 孩子兄弟表示类的定义template <class T> class CSTree

{ CSNode<T> *root, *current; int size; CSNode<T> *GetCSNode(const T& item, CSNode<T> *fcptr,CSNode<T> *nsptr);

void FreeCSNode(CSNode<T> *p); CSNode<T> *CopyTree(CSNode<T> *t); void DeleteTree(CSNode<T> *t); CSNode<T> *FindNode(const T& item, CSNode<T>* & pre) const;

Page 111: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

public:

CSTree(void);

CSTree(const CSTree<T>& tree);

~CSTree(void); CSTree<T>& operator= (const CSTree<T>& rhs);

int Find(T& item);

void Insert(const T& item);

void Delete(const T& item);

void ClearList(void);

int ListEmpty(void) const;

int ListSize(void) const;

void Update(const T& item);

CSNode<T> *GetRoot(void) const;

};

Page 112: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

森林与二叉树的转换

A

B C D

E

F

G

H I

J

A

B

C

D

E

F

G

H I

J

Page 113: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

森林与二叉树的转换的形式定义 森林转换成二叉树的规则: 设 F={T1,T2,···Tm} 是森林, 可以转换成二叉树 B={root, LB, RB}

(1) F 空, m=0, 则 B 是空树; (2) F 非空, m≠0,

则 root=T1 的根, LB 是 T1 去掉根后的子树组成的森林 F1={T11,T12,···,T1m} 转换成的二叉树; RB 是森林 F’={T2,T3,···,Tm} 转换成的二叉树。

Page 114: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉树转换成森林的规则: (递归定义)

设 B={root, LB, RB} 是二叉树 可以转换成森林 F={T1,T2,···Tm} , (1) B 是空树,则 F 空; (2) B 非空, 则 m≠0, T1 的根 =root , T1 去掉根后的子树组成的 森林 F1={T11,T12,···,T1m} 是 LB 转换成的; 森林 F’={T2,T3,···,Tm} 是 RB 转换成的。

Page 115: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

树的遍历(与二叉树不同)深度优先 1 。先根遍历 先访问根结点, 再先根遍历根的每一棵子树。 2 。 后根遍历 先后根遍历根的每一棵子树, 再访问根。

二叉树的先根遍历就是先序遍历

后根遍历就是后序遍历

Page 116: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

广度优先

先访问根结点 再依次访问第一层所有结点 再依次访问第二层所有结点 ···· 直到访问完所有结点

Page 117: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

先根遍历ABEFCDGHI

A

B C D

E F G

H I

后根遍历EFBCHIGDA

广度优先

ABCDEFGHI

Page 118: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

先根遍历的算法void PreOrderTraverse(CSNode<T>*t,void visit(T item))

{

if(t!=NULL)

{ visit(t->data);

PreOrderTraverse(t->firstChild,visit);

PreOrderTraverse(t->nextSibling,visit);

}

}

Page 119: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

森林的遍历

先序遍历森林 若森林非空 先访问第一棵树的根结点, 再先序遍历第一棵树根的子森林, 再先序遍历除去第一棵树后剩余 的子森林。 转换成二叉树后 与二叉树的先序遍历相同

Page 120: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

中序遍历森林

若森林非空中序遍历第一棵树根的子森林访问第一棵树的根中序遍历去掉第一棵树后剩余的子森林转换成二叉树后与二叉树的中序遍历相同

Page 121: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

后序遍历森林

若森林非空后序遍历第一棵树根的子森林后序遍历去掉第一棵树后剩余的子森林访问第一棵树的根

转换成二叉树后与二叉树的后序遍历相同

Page 122: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

先序遍历

A

B C D

E

F

G

H I

J

ABCDEFGHIJ

中序遍历BCDAFEHJIG

后序遍历DCBFJIHGEA

Page 123: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B

C

D

E

F

G

H I

J

先序遍历

ABCDEFGHIJ

中序遍历BCDAFEHJIG

后序遍历DCBFJIHGEA

Page 124: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

七、堆 Heap ( 改进了的树型排序)

(极小)堆的定义:

n 个元素的完全二叉树,每个结点都小于其子结点。

13

49 27

97 65 7638

Page 125: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

用一维数组表示极小堆 0 1 2 3 4 5 6

13 49 27 97 65 38 76

13

49 27

97 65 7638

A[i] ≤A[2i+1], A[i] ≤A[2i+1+1],

Page 126: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

堆排序

堆排序的过程 1. 将一个无序序列建成堆, 2. 输出顶点元素后,调整并重建堆, 3. 重复 2. 直至全部元素都输出完毕。

Page 127: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

先作第二步:输出并调整重建堆

13

49 27

97 65 7638

输出堆顶元素 13 ,用堆末元素取代,

将元素 76 下移与子结点中较小的一个交换,向下过滤,

直至叶结点,重复第 2 步直至全部输出。

76

13 7676

7627

27 76

7676

38

76

38

Page 128: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

第一步 建堆 从最末一个非叶结点 A[n-1/2] 开始向下过滤直至堆顶

49

38 65

97 76 2713

49

49

97

13

65

13

49

49

27

Page 129: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

#include <iostream.h>#include <stdlib.h>

template <class T> class Heap{private: T *hlist; int inArray; // max number allowed and current size of heap int maxheapsize; int heapsize; // identifies end of list // error message utility function void error(char errmsg[]); // utility functions for Delete/Insert to restore heap void FilterDown(int i); void FilterUp(int i);

Page 130: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

public: Heap(int maxsize); // create empty heap Heap(T arr[],int n); // "heapify" arr Heap(const Heap<T>& H); // copy constructor

~Heap(void); // destructor // overloaded operators: "=", "[]", "T*" Heap<T>& operator= (const Heap<T>& rhs); const T& operator[](int i); // list methods int ListSize(void) const; int ListEmpty(void) const; int ListFull(void) const; void Insert(const T& item); T Delete(void); void ClearList(void);};

Page 131: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// print error message and terminate the program

template <class T>

void Heap<T>::error(char errmsg[])

{

cerr << errmsg << endl;

exit(1);

}

Page 132: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void Heap<T>::FilterDown (int i){ int currentpos, childpos; T target; // start at i and set its value as the target currentpos = i; target = hlist[i]; childpos = 2 * i + 1; while (childpos < heapsize) // check for end of list

{ if ((childpos+1 < heapsize) && (hlist[childpos+1] <= hlist[childpos])) childpos = childpos + 1; if (target <= hlist[childpos]) break; else { hlist[currentpos] = hlist[childpos]; currentpos = childpos; childpos = 2 * currentpos + 1; } } hlist[currentpos] = target; }

Page 133: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>void Heap<T>::FilterUp (int i){ int currentpos, parentpos; T target; currentpos = i; parentpos = (i-1)/2; target = hlist[i]; while (currentpos != 0) { if (hlist[parentpos] <= target) break; else { hlist[currentpos] = hlist[parentpos]; currentpos = parentpos; parentpos = (currentpos-1)/2; } } hlist[currentpos] = target;}

Page 134: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>Heap<T>::Heap(int size){ if (size <= 0) error("Bad list size."); hlist = new T[size]; if (hlist == 0) error("Memory allocation failure."); maxheapsize = size; heapsize = 0; inArray = 0;}

Page 135: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>Heap<T>::Heap(T arr[],int n){ int currentpos; if (n <= 0) error("Bad list size."); maxheapsize = n; heapsize = n ; hlist = arr; currentpos = (heapsize – 1-1)/2; while(currentpos >= 0) { FilterDown(currentpos); currentpos--; } inArray = 1;}

Page 136: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>Heap<T>::Heap(const Heap<T>& A){ int n = A.heapsize; heapsize = n; maxheapsize = A.maxheapsize; hlist = new T[maxheapsize]; if (hlist == 0) error("Memory allocation failure."); while (n--) hlist[n] = A.hlist[n]; inArray = A.inArray; // always build new heap in dynamic memory}

Page 137: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

Heap<T>::~Heap(void)

{

if (!inArray) // don't delete hlist unless allocated.

delete[] hlist;

}

Page 138: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T> Heap<T>& Heap<T>::operator=(const Heap<T>& rhs)

{ int n = rhs.heapsize; heapsize = n; if (maxheapsize != rhs.maxheapsize || inArray) { maxheapsize = rhs.maxheapsize; if (!inArray) delete [] hlist; hlist = new T[maxheapsize]; // allocate a new array

if (hlist == NULL) error("Memory allocation failure."); } while (n--) // do item by item copy hlist[n] = rhs.hlist[n]; inArray = rhs.inArray; return *this;}

Page 139: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

int Heap<T>::ListSize(void) const

{

return heapsize;

}

template <class T>

int Heap<T>::ListEmpty(void) const

{

return heapsize == 0;

}

Page 140: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>int Heap<T>::ListFull(void) const{ return heapsize == maxheapsize;}

template <class T>const T& Heap<T>::operator[](int i){ if (i < 0 || i >= heapsize) error("Heap index out of range: "); return hlist[i];}

Page 141: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

void Heap<T>::Insert(const T& item)

{

// check for a full heap and terminate if True

if (heapsize == maxheapsize)

error("Heap full"); // store the item at the end of the heap and increment

// heapsize; call FilterUp to restore the heap condition

hlist[heapsize] = item;

FilterUp(heapsize);

heapsize++;

}

Page 142: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

T Heap<T>::Delete(void)

{ T tempitem;

if (heapsize == 0) error("Heap empty");

// copy the root to tempitem; replace the root with the last

// value in the heap and decrement the heap size

tempitem = hlist[0];

hlist[0] = hlist[heapsize-1];

heapsize--;

// call FilterDown to position the new root value in heap

FilterDown(0);

// return the original root value

return tempitem; }

Page 143: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

template <class T>

void Heap<T>::ClearList(void)

{

heapsize = 0;

}

#endif // HEAP_CLASS

Page 144: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

#include "heap.h"template <class T>void HeapSort (T A[], int n){ Heap<T> H(A,n); T elt; // iteration that loads element A[n-1] ... A[1] for(int i = n-1;i >= 0;i--) { elt = H.Delete( ); A[n-i-1] = elt; }}#endif

Page 145: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

八、霍夫曼树及其应用

最优二叉树 ---霍夫曼树

一棵树上两个结点间的路径:

从一个结点到另一个结点之间所有的分支构成路径。

路径长度:路径上分支的数目。

树的路径长度:树根到树的每一个结点的

路径长度之和。

Page 146: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

A

B C D

E F G

H I

D 到 H 的路径 DGH

D 到 H路径的长为 2

A 到 H 的路径 ADGH

A 到 H路径的长为 3

树的路径长度 =1+1+1+2+2+2+3+3=15

n 个结点的二叉树路径长度最小的是完全二叉树

Page 147: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

二叉树带权路径的长

A

B

C

D

E F G

H I

每个叶子结点都带权的二叉树叫带权二叉树

7

5

2 4

叶结点 C 的权为 7 ,根结点A 到 C走 7 次的路径长度称为 A 到 C 的带权路径的长

根到每个叶的带权路径的长的总和叫二叉树的带权路径的长

本树带权路径的长 =7*3+5*2+2*3+4*3=49

带权路径的长 WPL=Σ wklkk=1

n

Page 148: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

n 个叶结点 ,权分别为 w1,w2,···,wn 的二叉树中带权路径长度 WPL 最小的二叉树叫最优二叉树 也叫霍夫曼树

5

27 4

5 27 4

WPL=49

WPL=(7+5+2+4)*2

=36

Page 149: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

5 2 47 5

2 4

7

WPL=36 WPL=7+5*2+2*3+4*3

=35

霍夫曼树

Page 150: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

带权路径长度的例子

将百分制转换成五级计分制

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

b=‘E’

a<70

b=‘D’

a<80

b=‘C’

a<90

b=‘B’

b=‘A’

100 个学生中 5 个不及格 15 个60几分 40 个 70几分 30 个 80几分 10 个 90几分

5

15

40

30 10

需比较 5+2*15+3*40+4*30+4*10

=315 次

Page 151: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

a<60

b=‘E’

a<70

b=‘D’

a<80

b=‘C’

a<90

b=‘B’

b=‘A’

5

15

40

30a<80

b=‘C’

a<90

b=‘B’

b=‘A’

a<60

b=‘E’

a<70

b=‘D’

If(a<80)

if(a<70)

if(a<60)b=‘E’;

else b=‘D’;

else b=‘C’;

else if(a<90)b=‘B’;

else b=‘A’;

比较次数 =2*(10+30+40)+3*(15+5)

=160+60=220

Page 152: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

霍夫曼算法1 )根据给定的权值 {w1,w2,···,wn}构造 n 个 二叉树 F={T1,T2,···,Tn} 每个 Ti 只有一个 根结点,权为 wi 。2 )在 F 中选取两棵根结点的权值最小的树 构成一棵新的二叉树,其根的权值为左 右子树根的权值的和。3 ) F 中删去这两棵树,加上新得的树。4 )重复 2 ) 3 )直到只剩一棵树。

Page 153: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

5 27 4A B C D

C D

657A B

C D

B

A

117

C D

B

A

Page 154: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

霍夫曼编码 --- 带权路径长度的应用 数字通信 传输电文 ABACCDA 前缀码编码方案

A B C D

00 01 10 11

电文 00010010101100 14 位前缀码:每一个字符的编码都不是另一个字符编码的前缀

A: 0 B: 00 C: 1 D: 01 不是前缀码引起混淆

Page 155: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

用二叉树设计前缀码

电文 ABACCDA 01001101101110 14 位

C D

B

A

0 1

0 1

0 1

A B C D

0 10 110 111

Page 156: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

霍夫曼编码 --- 使电文总长最短的二进制编码 电文 ABACCDA

字符出现的频度 A 4 次 B 1 次 C 2 次 D 1 次 构造一棵权为 {4 , 1 , 2 , 1} 的霍夫曼树

B D

C

A

0 1

0 1

0 1

A B C D

0 110 10 111

电文 0110010101110 13位

Page 157: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

//赫夫曼树和赫夫曼编码的存储表示#include<string.h>#define Max 1000typedef char * *HuffmanCode; //动态分配数组存储赫夫曼编码表

class HTNode { public:

unsigned weight;unsigned parent,lchild,rchild;HTNode( ):weight(0),parent(0),

lchild(0),rchild(0){} };

Page 158: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

weight parent lchild rchild

1 2

3

4

5

6

7

8

9

10

5 0 0 0

29 0 0 0

7

8

14

23

3

11

Page 159: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// 从 k 个结点中选取权重最小的两个根结点 s1,s2

void Select(HTNode * HT, int k ,int &s1,int &s2){ int i; s1=s2=0;

HT[s1].weight=HT[s2].weight=Max; for(i=1;i<=k;i++)

{ if(HT[i].parent==0) if(HT[i].weight<HT[s1].weight)

{ s1=i; s2=s1; } else if(HT[i].weight<HT[s2].weight)s2=i;

}}

Page 160: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

//求赫夫曼编码的算法void HuffmanCoding(HTNode*&HT, HuffmanCode &HC, int *w,int n){ //w 存放 n 个字符的权值 (均> 0) ,构造赫夫曼树 HT , // 并求出 n 个字符的赫夫曼编码 HC 。 int c,f,i,m,s1,s2,start;

char *cd;HTNode *p;if(n<=1)return;m=2*n-1;HT=new HTNode[m+1];

//动态分配数组存储赫夫曼树 0号单元不用for(p=HT,i=1;i<=n;++i,++p,++w) p->weight=*w;

Page 161: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

for(i=n+1;i<=m;++i) // 建赫夫曼树 { Select(HT,i-1,s1,s2); //选择 parent 为 0 且 weight 最小的两个结点, // 其序号分别为: 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; }

Page 162: 树和 二叉树   二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数  堆  树与森林  霍夫曼树及其应用

// 从叶子到根逆向求每个字符的赫夫曼编码HC=new (char**)[n+1]; // 分配 n 个字符编码的头指针向量cd=new (char *)[n]; // 分配求编码的工作空间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) // 从叶子到根逆向求编码写入 cd

if(HT[f].lchild==c)cd[--start]=‘0’; else cd[--start]=‘1’; HC[i]=new(char*)[n-start];// 为第 i 个字符编码分配空间 strcpy(HC[i],&cd[start]); // 从 cd 复制编码 ( 串 ) 到 HC[i] } delete cd; //释放工作空间 }