树和 二叉树 二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数 堆 ...
DESCRIPTION
第六章 树和森林. 树和 二叉树 二叉树遍历 线索二叉树 二叉搜索树 二叉树的计数 堆 树与森林 霍夫曼树及其应用. 一、树和二叉树. 树 tree 的定义 (1) 无结点的树 空树 (2) 非空树 仅有一个 根 结点 其余结点分为若干 互不相交的 子树. A. ········································ 根. 3 度. D. B. C. H. I. J. - PowerPoint PPT PresentationTRANSCRIPT
树和 二叉树 树和 二叉树 二叉树遍历二叉树遍历 线索二叉树线索二叉树 二叉搜索树二叉搜索树 二叉树的计数二叉树的计数 堆堆 树与森林树与森林 霍夫曼树及其应用 霍夫曼树及其应用
一、树和二叉树
树 tree 的定义 (1) 无结点的树 空树 (2) 非空树 仅有一个根结点 其余结点分为若干 互不相交的子树
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
A
B C D
E F G H I J
K L M
············································· 第一层 ······························第二层 ··············· 第三层
····································第四层
树的深度为4
A
B C
D E F G
H I J
2. 二叉树 每个结点至多有两棵子树 ,1 度或 2度
深度为 k ,结点至多 2k-1 个第 i 层结点至多 2i-1 个
设有 n2 个 2 度点
则有 n2+1 个叶片A
B C
D E F G
H I J
A
B C
D E F G
H I J
满二叉树
K L M N O
深度为 k, 结点数 2k-1, 每层结点数都最大
满二叉树 满二叉树去掉最下层最右边若干结点
满二叉树也是完全二叉树
完全二叉树
A
B C
D E F G
H I J K L M N O
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
1
2 3
4 5 6 7
8 9 10
完全二叉树
11 12 13
结点 i 的左子结点是 2i
右子结点是 2i+1
结点 i 的父结点是 [i/2]
二叉树的存储
1. 完全二叉树的顺序存储 #define MaxTreeSize 100
typedef TElemType SqBiTree[MaxTreeSize];
SqBiTree bt;
1 2 3 4 5 6 7 8 9 10 11 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
二叉树的链式存储
树结点 左指针 数据 右指针
lchild data rchild
A
B ^ C
^ D ^ E ^ F ^
^ G ^ ^ H ^
A
B C
D E F
G H
二叉树结点类的定义
#ifndef TREENODE_CLASS
#define TREENODE_CLASS
#ifndef NULL
const int NULL = 0;
#endif // NULL
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>;};
// 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)
{}
// 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;
}
// 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;
}
// 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
#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
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;}
建立一个结点
撤销一个结点
template <class T>
void FreeTreeNode(TreeNode<T> *p)
{
delete p;
}
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
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
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
为代数式 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;
二、二叉树的遍历先序遍历 PreOrderTraverse 先访问根结点 再访问左子树 后访问右子树中序遍历 InOrderTraverse 先访问左子树 再访问根结点 后访问右子树 后序遍历 PostOrderTraverse 先访问左子树 再访问右子树 后访问根结点层次遍历( Level Traverse )
先序遍历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;}
中序遍历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
后序遍历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
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++; }}
二叉树的运算
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;}
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;
}
template <class T>
void DeleteTree(TreeNode<T> *t){ if (t != NULL) { DeleteTree(t->Left()); DeleteTree(t->Right()); FreeTreeNode(t); }}
输入一棵树按先序输入一棵树键盘输入: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
template <class T>void ClearTree(TreeNode<T> * &t)
{
DeleteTree(t);
t = NULL; // root now NULL
}
#endif // TREE_LIBRARY_FUNCTIONS
层次遍历——广度优先法遍历树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
测试#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); }
三、线索二叉树
带线索的二叉树,即每个结点都带有指向其前驱和后继的指针的二叉树
lchild ltag data rtag rchild
ltag=0 lchild 指向左子结点 =1 前驱rtag=0 rchild 指向右子结点 =1 后继
优点:遍历查找方便快捷,从任何结点可
以向前向后搜索
用途:需要反复遍历和查找的二叉树
中序线索化树
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
线索二叉树的结点类#include<iostream.h>#include<stdio.h>
typedef enum{
Link, Thread //Link==0; Thread==1;}PointerTag;
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){ }
}
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
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; }
}
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); }
}
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;
}}
四、二叉搜索树 (二叉排序树) 其左非空子树上所有结点的值都小于根结点的值 其右非空子树上所有结点的值都大于根结点的值 左右子树也是二叉搜索树
45
12 57
8 20 60
3 11 59
50
二叉搜索树的作用:排序,检索
二叉搜索树#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"
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;
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;
};
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;}
// delete the storage occupied by a tree node
template <class T>void BinSTree<T>::FreeTreeNode(TreeNode<T> *p)
{
delete p;
}
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;}
template <class T>void BinSTree<T>::DeleteTree(TreeNode<T> *t)
{
if (t != NULL)
{
DeleteTree(t->left);
DeleteTree(t->right);
FreeTreeNode(t);
}
}
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;
}
template <class T>
BinSTree<T>::BinSTree(void): root(NULL),current(NULL),size(0)
{ }
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;
}
template <class T>
BinSTree<T>::~BinSTree(void)
{
// just call ClearList
ClearList( );
}
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;
}
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;}
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++;}
二叉搜索树的插入过程
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
二叉搜索树的删除过程先调用函数 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
二叉搜索树的删除过程先调用函数 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;
二叉搜索树的删除过程先调用函数 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
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
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 在左
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; }
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--;
}
template <class T>void BinSTree<T>::ClearList(void){ DeleteTree(root); root = NULL; current = NULL; size = 0;}
template <class T>
int BinSTree<T>::ListEmpty(void) const
{
return root == NULL;
}
template <class T>
int BinSTree<T>::ListSize(void) const
{
return size;
}
// 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);}
template <class T>TreeNode<T> *BinSTree<T>::GetRoot(void) const
{
return root;
}
#endif
// BINARY_SEARCH_TREE_CLASS
测试练习
1 。用二叉搜索树为一个数列排序, 分别用前序和后序再输出。
2 。键盘输入若干个字符串,排序后输出。
3 。在输入的字符串中删除两个串,再输出。
练习
一 . 画出以下输入所对应二叉搜索树: 1 ) R,O,T,A,R,Y,C,L,U,B
2 ) 60 , 25 , 7 , 99 , 15 , 3 , 10
38 , 59 , 62 , 34
二 . 分别给出这两棵树的前序,中序,后序遍历输出
练习
一棵二叉树,先序序列为 EBADCFHGIKJ
中序序列为 ABCDEFGHIJK
画出这棵树
五、树的计数有 n 个结点的不同形态的二叉树有多少?不考虑结点的数据的异同树 T 和 T’ 相似: T 和 T’ 都是空树或两者都不是空树且 左右子树分别相似。设 n 个结点互不相似的二叉树的数目为 bn
则 b0=1, b1=1, b2=2 。
b1=1
b2=2
b3=5=b2*b0+b1*b1+b0*b2
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
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
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
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!
设 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+···
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+···
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
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
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
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
树的计数
树可以转换成唯一的二叉树
即不同的树转换成不同的二叉树
不同的二叉树也可以转换成不同的树
只要根结点没有左子树
因此 n 个结点的树与 n-1 个结点的二叉树
的数目相同
tn=bn-1
t1=1
t2=1
t3=2
t4=b3
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
n 个元素进栈,有多少不同的出栈方法
记 n 个元素进栈有 bn 种出栈法
bn=Σbkbn-k-1k=0
n-1
n+11 C2n
nbn= =C2nn - C2n
n-1
六、树和森林
若干棵树组成森林
树的存储结构
双亲表示法 孩子表示法 孩子兄弟表示法
双亲表示法
用数组存储树的结点每个结点中附设一个字段指示其父结点的位置
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
双亲表示法结点定义#define MAX_TREE_SIZE 100template <class T>class PNode { T data; int Parent; public: PNode(T item, int pr); T GetData( ); int GetParent( ); }}
树的双亲表示法定义
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( ); }
孩子表示法
1. k 叉树结点表示 ( 固定长结点)
A
B C D
E F G
H I
data child1 child2 ······ childk
A
B C D
E F G
H I
孩子表示法
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
孩子表示法
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
孩子链表表示结点类的定义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; ……};
带双亲的
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 ^
孩子表示法
孩子兄弟表示法(二叉树表示法)
结点 : 数据 孩子 兄弟
A
B C D
E F G
H I
data firstchild nextsibling
A
B C D
E F G
H I
孩子兄弟表示法(二叉树表示法)
结点 : 数据 孩子 兄弟
A
B C D
E F G
H I
data firstchild nextsiblingA
B
C
D
E
F
G
H
I
// 孩子兄弟表示结点类的定义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; };
// 孩子兄弟表示类的定义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;
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;
};
森林与二叉树的转换
A
B C D
E
F
G
H I
J
A
B
C
D
E
F
G
H I
J
森林与二叉树的转换的形式定义 森林转换成二叉树的规则: 设 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} 转换成的二叉树。
二叉树转换成森林的规则: (递归定义)
设 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 转换成的。
树的遍历(与二叉树不同)深度优先 1 。先根遍历 先访问根结点, 再先根遍历根的每一棵子树。 2 。 后根遍历 先后根遍历根的每一棵子树, 再访问根。
二叉树的先根遍历就是先序遍历
后根遍历就是后序遍历
广度优先
先访问根结点 再依次访问第一层所有结点 再依次访问第二层所有结点 ···· 直到访问完所有结点
先根遍历ABEFCDGHI
A
B C D
E F G
H I
后根遍历EFBCHIGDA
广度优先
ABCDEFGHI
先根遍历的算法void PreOrderTraverse(CSNode<T>*t,void visit(T item))
{
if(t!=NULL)
{ visit(t->data);
PreOrderTraverse(t->firstChild,visit);
PreOrderTraverse(t->nextSibling,visit);
}
}
森林的遍历
先序遍历森林 若森林非空 先访问第一棵树的根结点, 再先序遍历第一棵树根的子森林, 再先序遍历除去第一棵树后剩余 的子森林。 转换成二叉树后 与二叉树的先序遍历相同
中序遍历森林
若森林非空中序遍历第一棵树根的子森林访问第一棵树的根中序遍历去掉第一棵树后剩余的子森林转换成二叉树后与二叉树的中序遍历相同
后序遍历森林
若森林非空后序遍历第一棵树根的子森林后序遍历去掉第一棵树后剩余的子森林访问第一棵树的根
转换成二叉树后与二叉树的后序遍历相同
先序遍历
A
B C D
E
F
G
H I
J
ABCDEFGHIJ
中序遍历BCDAFEHJIG
后序遍历DCBFJIHGEA
A
B
C
D
E
F
G
H I
J
先序遍历
ABCDEFGHIJ
中序遍历BCDAFEHJIG
后序遍历DCBFJIHGEA
七、堆 Heap ( 改进了的树型排序)
(极小)堆的定义:
n 个元素的完全二叉树,每个结点都小于其子结点。
13
49 27
97 65 7638
用一维数组表示极小堆 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],
堆排序
堆排序的过程 1. 将一个无序序列建成堆, 2. 输出顶点元素后,调整并重建堆, 3. 重复 2. 直至全部元素都输出完毕。
先作第二步:输出并调整重建堆
13
49 27
97 65 7638
输出堆顶元素 13 ,用堆末元素取代,
将元素 76 下移与子结点中较小的一个交换,向下过滤,
直至叶结点,重复第 2 步直至全部输出。
76
13 7676
7627
27 76
7676
38
76
38
第一步 建堆 从最末一个非叶结点 A[n-1/2] 开始向下过滤直至堆顶
49
38 65
97 76 2713
49
49
97
13
65
13
49
49
27
#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);
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);};
// print error message and terminate the program
template <class T>
void Heap<T>::error(char errmsg[])
{
cerr << errmsg << endl;
exit(1);
}
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; }
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;}
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;}
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;}
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}
template <class T>
Heap<T>::~Heap(void)
{
if (!inArray) // don't delete hlist unless allocated.
delete[] hlist;
}
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;}
template <class T>
int Heap<T>::ListSize(void) const
{
return heapsize;
}
template <class T>
int Heap<T>::ListEmpty(void) const
{
return heapsize == 0;
}
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];}
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++;
}
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; }
template <class T>
void Heap<T>::ClearList(void)
{
heapsize = 0;
}
#endif // HEAP_CLASS
#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
八、霍夫曼树及其应用
最优二叉树 ---霍夫曼树
一棵树上两个结点间的路径:
从一个结点到另一个结点之间所有的分支构成路径。
路径长度:路径上分支的数目。
树的路径长度:树根到树的每一个结点的
路径长度之和。
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 个结点的二叉树路径长度最小的是完全二叉树
二叉树带权路径的长
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
n 个叶结点 ,权分别为 w1,w2,···,wn 的二叉树中带权路径长度 WPL 最小的二叉树叫最优二叉树 也叫霍夫曼树
5
27 4
5 27 4
WPL=49
WPL=(7+5+2+4)*2
=36
5 2 47 5
2 4
7
WPL=36 WPL=7+5*2+2*3+4*3
=35
霍夫曼树
带权路径长度的例子
将百分制转换成五级计分制
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 次
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
霍夫曼算法1 )根据给定的权值 {w1,w2,···,wn}构造 n 个 二叉树 F={T1,T2,···,Tn} 每个 Ti 只有一个 根结点,权为 wi 。2 )在 F 中选取两棵根结点的权值最小的树 构成一棵新的二叉树,其根的权值为左 右子树根的权值的和。3 ) F 中删去这两棵树,加上新得的树。4 )重复 2 ) 3 )直到只剩一棵树。
5 27 4A B C D
C D
657A B
C D
B
A
117
C D
B
A
霍夫曼编码 --- 带权路径长度的应用 数字通信 传输电文 ABACCDA 前缀码编码方案
A B C D
00 01 10 11
电文 00010010101100 14 位前缀码:每一个字符的编码都不是另一个字符编码的前缀
A: 0 B: 00 C: 1 D: 01 不是前缀码引起混淆
用二叉树设计前缀码
电文 ABACCDA 01001101101110 14 位
C D
B
A
0 1
0 1
0 1
A B C D
0 10 110 111
霍夫曼编码 --- 使电文总长最短的二进制编码 电文 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位
//赫夫曼树和赫夫曼编码的存储表示#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){} };
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
// 从 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;
}}
//求赫夫曼编码的算法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;
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; }
// 从叶子到根逆向求每个字符的赫夫曼编码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; //释放工作空间 }