數(shù)據(jù)結(jié)構(gòu)(C語言版) 第6章 樹
《數(shù)據(jù)結(jié)構(gòu)(C語言版) 第6章 樹》由會(huì)員分享,可在線閱讀,更多相關(guān)《數(shù)據(jù)結(jié)構(gòu)(C語言版) 第6章 樹(63頁珍藏版)》請(qǐng)?jiān)谘b配圖網(wǎng)上搜索。
1、第6章 樹 1樹的邏輯結(jié)構(gòu) 核心關(guān)系:層次關(guān)系。 1.1 通俗定義 樹形表示法 廣義表表示法 A(B(E,F,G),C(H),D(I,J)) 樹是n(n>0)個(gè)結(jié)點(diǎn)的有限集,在任意一棵樹中: 1 有且只有一個(gè)特定的根(root)結(jié)點(diǎn); 2 當(dāng)n>1時(shí),其余結(jié)點(diǎn)可分為m(m>0)個(gè)互不相交的有限子集T1,T2...Tm,每個(gè)子集是一棵樹,稱為子樹。 1.2 形式定義 D={ai | ai∈ElemSet,i=1,…,n} 二元關(guān)系S的定義: 當(dāng)n=1時(shí),S=φ; 當(dāng)n>1時(shí): 1 根的特殊地位 唯一無前驅(qū)的元素:根結(jié)點(diǎn)(root)
2、;
2
結(jié)點(diǎn)的劃分
可將D-{root}劃分成m(m>0)個(gè)互不相交的子集D1,D2,…Dm,對(duì)任意子集Di,唯一存在xi∈Di,有
3、D,I>,
4、 葉子結(jié)點(diǎn) 度為0的結(jié)點(diǎn)。 分支結(jié)點(diǎn) 度不為0的結(jié)點(diǎn)。 樹的度 樹內(nèi)所有結(jié)點(diǎn)的度的最大值。 1.3.3 與“深度”相關(guān)的概念 結(jié)點(diǎn)深度 根結(jié)點(diǎn)的深度為1 若某結(jié)點(diǎn)深度為i, 則其子結(jié)點(diǎn)深度為i+1 樹的深度 樹中結(jié)點(diǎn)的最大深度。 或 空樹深度為0; 非空樹深度等于子樹深度的最大值加1。 1.3.4 其他概念 有序樹、無序樹 左右結(jié)點(diǎn)是否等價(jià) 森林 m棵互不相交的樹的集合。 m=0:空集; m=1:樹 1.4 基本操作 構(gòu)造、析構(gòu)、遍歷、查找、插入、刪除、 2 二叉樹的邏輯結(jié)構(gòu) 2.1 定義 結(jié)構(gòu)簡(jiǎn)化、
5、概念強(qiáng)化:左子樹、右子樹。
樹形表示法
廣義表表示法
A(B(D),C(F(,E),G))
2.2 二叉樹的形態(tài)
具有n個(gè)結(jié)點(diǎn)的不同形態(tài)的二叉樹有多少顆?
二叉樹相似:二者都為空;或它們的左右子樹相似。
例:n個(gè)結(jié)點(diǎn)的相似樹的個(gè)數(shù)
template
6、reeCount(left) * GetTreeCount(n-1-left); return(count); } 2.3 性質(zhì) 2.3.1 性質(zhì)1 若某二叉樹的葉子結(jié)點(diǎn)數(shù)為n0,度為2的結(jié)點(diǎn)數(shù)為n2,則n0=n2+1。 試題例:若一棵m叉樹中度為i的結(jié)點(diǎn)有Ni個(gè),則該樹的葉結(jié)點(diǎn)有 (N1+2N2+…+mNm+1)-(N1+N2+…+Nm) 個(gè)。 2.3.2 性質(zhì)2 二叉樹的第i層上的結(jié)點(diǎn)數(shù)最多為2i-1。 2.3.3 性質(zhì)3 深度為k的二叉樹中結(jié)點(diǎn)總數(shù)最多為2k-1。 滿二叉樹 完全二叉樹 深度為k,共有2k-1個(gè)結(jié)點(diǎn)。 深
7、度為k,前k-1層是滿二叉樹,第k層的結(jié)點(diǎn)從左至右依次排列。
2.3.4 性質(zhì)4
具有n個(gè)結(jié)點(diǎn)的完全二叉樹的深度為int(log2n)+1。
2.3.5 性質(zhì)5
有n個(gè)結(jié)點(diǎn)的完全二叉樹,結(jié)點(diǎn)從0順序標(biāo)號(hào),則:
1、若i>0,i的雙親結(jié)點(diǎn)是(i-1)/2。
2、若2i+1 8、結(jié)構(gòu)存于數(shù)組A[0…100]中,葉子結(jié)點(diǎn)的最小編號(hào)是 。
A、32 B、33 C、34 D、35
試題例:具有101個(gè)結(jié)點(diǎn)的完全二叉樹,度為1的結(jié)點(diǎn)有 個(gè)。
3.1.2 性能分析
滿/完全二叉樹:存儲(chǔ)效率最高,插入、刪除方便。
非完全二叉樹的處理方法:
非完全二叉樹
修補(bǔ)結(jié)構(gòu)
不存在的結(jié)點(diǎn),用特殊符號(hào)標(biāo)識(shí)。
退化的二叉樹及其存儲(chǔ)效果:
評(píng)價(jià):空間浪費(fèi)大,不靈活。
3.2 鏈?zhǔn)浇Y(jié)構(gòu)
3.2.1 二叉鏈表結(jié)構(gòu)
Template 9、e
{ T data;
BinTreeNode 10、
邏輯結(jié)構(gòu)
存儲(chǔ)結(jié)構(gòu)
3.2.3 三叉靜鏈表結(jié)構(gòu)
Template 11、ag {LEFT,NOLEFT};
Template 12、 p->child=q; q->right=r; r->right=p;
二、結(jié)構(gòu)示例
二叉鏈表結(jié)構(gòu)
三、基本操作
①求結(jié)點(diǎn)*p的左孩子*p_LeftChild。
if(p->type==LEFT) p_LeftChild=p->child;
②求結(jié)點(diǎn)*p的右孩子*p_RightChild。
if(p->type==LEFT && p->child->right!=p)
p_RightChild=p->child->right;
或 if(p->type==NOLEFT && p->child!=NULL)
13、
p_RightChild=p->child;
③當(dāng)p->right!=NULL時(shí),求結(jié)點(diǎn)*p的父結(jié)點(diǎn)*p_Parent。
設(shè)q=p->right
if(q->child==p) p_Parent=q; // *p是獨(dú)左/右子
if(q->child==NULL) p_Parent=q->right; // *p和*q是兄弟
if(q->child!=NULL && q->child->right==p) p_Parent=q;
//q->child指向左孩子,p指向右孩子
if(q-> 14、right==NULL) p_Parent=q; // *p_Parent是根
if(q->right!=NULL && q->right->child==p)
p_Parent=q->right; // *p是左孩子,*q是右孩子
四、結(jié)構(gòu)優(yōu)點(diǎn)
查找父結(jié)點(diǎn)的時(shí)間復(fù)雜度為O(1);
遍歷二叉樹,不再需要棧,提高了指針的利用率。
第6章 樹
4 遍歷二叉樹
遍歷:每個(gè)結(jié)點(diǎn)訪問且只訪問一次。
template 15、
public:
void TraverseDFS(int kind); // 深度遍歷二叉樹
void TraverseBFS(); // 層次(廣度)遍歷二叉樹
private:
void TraversePre(BinTreeNode 16、叉樹的遍歷轉(zhuǎn)變?yōu)樽訕涞谋闅v。
先序算法:根左右
中序算法:左根右
后序算法:左右根
①若二叉樹為空,結(jié)束
②訪問根結(jié)點(diǎn)
③先序遍歷左子樹
④先序遍歷右子樹
①若二叉樹為空,結(jié)束
②中序遍歷左子樹
③訪問根結(jié)點(diǎn)
④中序遍歷右子樹
①若二叉樹為空,結(jié)束
②后序遍歷左子樹
③后序遍歷右子樹
④訪問根結(jié)點(diǎn)
先序序列:根左右
ABDECF
中序序列:左根右
DBEAFC
后序序列:左右根
DEBFCA
遍歷序列與二叉樹不是一一對(duì)應(yīng)的。
例:若前序序列為123,對(duì)應(yīng)的二叉樹有5種。
試題 17、例:試找出分別滿足下列條件的所有二叉樹
①先序序列和中序序列相同
②中序序列和后序序列相同
③先序序列和后序序列相同
4.1.2 算法實(shí)現(xiàn)、調(diào)試、理解
template 18、
cout< 19、③P(&B)
①② =>'B'
③P(&D)
①② =>'D'
③P(NULL);④P(NULL)
④P(&E)
①② =>'E'
③P(NULL);④P(NULL)
④P(&C)
①② =>'C'
③P(&F)
①② =>'F'
③P(NULL);④P(NULL)
④P(NULL)
性能分析:(設(shè)結(jié)點(diǎn)數(shù)為n,樹深度為k)
時(shí)間復(fù)雜度
O(n)
空間復(fù)雜度
O(k) 棧的 20、最大深度等于樹深度
4.1.3 算法的另一種實(shí)現(xiàn)
template 21、
void BinTree 22、
template 23、if(p->lchild) Q.push(p->lchild);
if(p->rchild) Q.push(p->rchild);
}
cout< 24、avFlag{START,LEFT,RIGHT};
template 25、ot,START}進(jìn)棧;
②取棧頂狀態(tài),設(shè)為s;
③若s的當(dāng)前方向是START,
若存在左孩子,則將{左孩子,START}進(jìn)棧;
若s的當(dāng)前方向是START,
若存在右孩子,則將{右孩子,START}進(jìn)棧;
④若棧不空,則循環(huán)②③,否則遍歷完成。
template 26、) )
{ Status 27、g(); // 調(diào)整棧頂狀態(tài)的方向
break;
case LEFT:
if(kind==2) cout< 28、IGHT:
if(kind==3) cout< 29、>::GetCount()
{ return Count(m_Root); }
template 30、TreeNode 31、t,right;
if(p==NULL) return(0);
if(root->lchild==NULL && root->rchild==NULL)
return 1;
left= Count(p->lchild);
right=Count(p->rchild);
return left+right;
}
5.1.2 層次遍歷模式
template 32、 33、整個(gè)樹的空間
5.2.1 深度遍歷模式
template 34、
template 35、 if(p->rchild) Q.push(p->rchild);
delete p;
}
}
5.3 計(jì)算二叉樹的深度/高度
template 36、ght=Height(p->rchild);
if (left>right) return left+1;
return right+1;
}
思考:與廣義表的深度算法的相似之處。
5.4 交換樹中每個(gè)結(jié)點(diǎn)的左右子樹
template 37、ge( p->lchild );
DoExchange( p->rchild );
BinTreeNode 38、nTree 39、
{ return DoSearchParent(m_Root, e); }
template 40、 e);
if(q) return q;
return DoSearchParent(p->rchild, e);
}
5.5.3 思考題: 查找所有值為e的結(jié)點(diǎn),刪除以該結(jié)點(diǎn)為根的子樹。
template 41、 NULL;
if(p->lchild && p->lchild->data==e)
{ DoFree(p->lchild); p->lchild=NULL; }
if(p->rchild && p->rchild->data==e)
{ DoFree(p->rchild); p->rchild=NULL; }
DoSearchDelete(p->lchild, e);
DoSearchDelete(p->rchild, e);
}
5.6 查找極值結(jié)點(diǎn)
template 42、>::GetMax()
{ return GetMax(m_Root); }
template 43、
if(plMax==NULL) // 右子樹不為空
if(prMax->data > p->data) return prMax; else return p;
if(prMax==NULL) // 右子樹不為空
if(plMax->data > p->data) return plMax; else return p;
// 左右子樹均不為空
if(plMax->data > prMax->data) pMax=plMax; else pMax=prMax;
if(p->data > pMax->data) return p; el 44、se return pMax;
}
5.7 構(gòu)造函數(shù)(參數(shù):帶空指針標(biāo)記的先序序列)
先序序列:
ABDECF
帶空指針標(biāo)記的先序序列:
ABD**E**CF***
template 45、
{ T e=m_pre[m_prei]; m_prei++;
if(e=='*') return(NULL);
BinTreeNode 46、 F A E K C G
取A
取B
取H
取F
取D
取E
取C
取K
取G
5.8.2 算法與實(shí)現(xiàn)
遞歸算法的常識(shí):
組成
①分解策略,②最終小問題的解決方案。
外觀
大小問題必須具有完全相同的形式。
template 47、 n); // 調(diào)用核心函數(shù)
}
template 48、;
p->lchild = DoCreateByPreMid(ipre+1, imid, i);
p->rchild = DoCreateByPreMid(ipre+i+1,imid+i+1,n-i-1);
return p;
}
5.8.3 延伸思考
利用后序序列、中序序列,可以構(gòu)造二叉樹嗎?
利用先序序列、后序序列,可以構(gòu)造二叉樹嗎?
5.9 拷貝構(gòu)造函數(shù)
template 49、template 50、.10.1 存儲(chǔ)結(jié)構(gòu)的優(yōu)化
字符串存儲(chǔ)結(jié)構(gòu):在運(yùn)算中,需要識(shí)別運(yùn)算符、運(yùn)算數(shù);比較優(yōu)先級(jí)。效率低。
表達(dá)式樹的邏輯結(jié)構(gòu)
運(yùn)算符
運(yùn)算對(duì)象
左操作數(shù)
右操作數(shù)
分支結(jié)點(diǎn)
葉子結(jié)點(diǎn)
左子樹
右子樹
例:a+b*c-d
5.10.2 表達(dá)式樹的類定義
class BinTree
{
static char m_Priority[4][4];
BinTreeNode* m_Root;
public:
BinTree();
BinTree(char mid[]);
~BinTree();
vo 51、id Free(); // 釋放整個(gè)樹的空間
void TraverseDFS(int kind); // 深度遍歷二叉樹
double Value();
……………
}
5.10.3 遍歷的意義:求值
先序:前綴表達(dá)式
中序:中綴表達(dá)式
后序:后綴表達(dá)式
// 一次建樹,多次高效計(jì)算、轉(zhuǎn)換方便
double BinTree::Value()
{ return DoValue(m_Root); }
double BinTree::DoValue(BinTreeNode* p)
{ if(p->type==OPD) return(p->data); 52、
double opd1 = DoValue(p->lchild);
double opd2 = DoValue(p->rchild);
return Compute(opd1,p->op,opd2);
}
5.10.4 建樹算法
設(shè)已有表達(dá)式樹的根指針為m_Root,則每建立一個(gè)運(yùn)算符結(jié)點(diǎn)*op和運(yùn)算數(shù)結(jié)點(diǎn)*opd3時(shí),結(jié)點(diǎn)關(guān)系需做如下調(diào)整:
若*op優(yōu)先級(jí)高:*op作*m_Root的右孩子。
a+b
a+b*c
op->lchild=m_Root->rchild;
op->rchild=opd3;
m_Root->rchild= 53、op;
若*op優(yōu)先級(jí)低:*m_Root作為*op的左孩子。
a*b
a*b-c
op->lchild=m_Root;
op->rchild=opd3;
m_Root=op;
5.10.5 算法實(shí)現(xiàn)
理解次序:
1 設(shè)表達(dá)式不含括號(hào),忽略代碼中所有紅字部分,即實(shí)現(xiàn)簡(jiǎn)單表達(dá)式的建樹過程。
2 考慮任一運(yùn)算數(shù)都可能是括號(hào)表達(dá)式的情形,則紅字部分的遞歸調(diào)用實(shí)現(xiàn)了復(fù)雜表達(dá)式的建樹過程。
BinTree::BinTree(char mid[])
{ m_midi=0; // 與mid配合使用的下標(biāo)變量
m_Root = DoCreate( 54、mid);
}
BinTreeNode* BinTree::DoCreate(char mid[])
{ // 初始化最初始的樹(3個(gè)結(jié)點(diǎn))
BinTreeNode *root,*opd1,*opd2;
if(mid[m_midi]=='(')
{ m_midi++; opd1=DoCreate(mid); }
else
{ opd1=new BinTreeNode; // 第1個(gè)運(yùn)算數(shù)結(jié)點(diǎn)
opd1->type=OPD; opd1->data=mid[m_midi++]-'0';
opd1->lchild= 55、opd1->rchild=NULL;
}
root = new BinTreeNode; // 第1個(gè)運(yùn)算符結(jié)點(diǎn)
root->type=OP; root->op=mid[m_midi++];
if(mid[m_midi]=='(')
{ m_midi++; opd2=DoCreate(mid); }
else
{ opd2=new BinTreeNode; // 第2個(gè)運(yùn)算數(shù)結(jié)點(diǎn)
opd2->type=OPD; opd2->data=mid[m_midi++]-'0';
opd2->lchild= 56、opd2->rchild=NULL;
}
root->lchild=opd1; root->rchild=opd2;
// 每躺循環(huán)增加2個(gè)結(jié)點(diǎn): *op和*opd3
while(mid[m_midi])
{ if(mid[m_midi]==')') { m_midi++; break; }
BinTreeNode *op=new BinTreeNode; // 運(yùn)算符結(jié)點(diǎn)
op->type=OP; op->op=mid[m_midi++];
BinTreeNode *opd3;
if(mid[m_midi]== 57、'(')
{ m_midi++; opd3=DoCreate(mid); }
else
{ opd3=new BinTreeNode; // 運(yùn)算數(shù)結(jié)點(diǎn)
opd3->type=OPD; opd3->data=mid[m_midi++]-'0';
opd3->lchild=opd3->rchild=NULL;
}
// 比較*root和*op的優(yōu)先級(jí)
if(Precede(root->op, op->op)=='<')
if(Precede(root->op, op->o 58、p)=='<')
{ op->lchild=root->rchild; op->rchild=opd3;
root->rchild=op;
}
else
{ op->lchild=root; op->rchild=opd3;
root=op;
}
}
return root;
}
5.9.6 體會(huì)
棧和樹是等價(jià)的。
作業(yè)與上機(jī)
1 二叉樹類
建立二叉樹類,應(yīng)包含以下成員函數(shù):構(gòu)造、深度/層次遍歷、查找、深度。
方向1:盡可能的豐富二叉樹類的成員函數(shù);
方向2:設(shè)計(jì)更多 59、拷貝構(gòu)造函數(shù)(實(shí)現(xiàn)類型轉(zhuǎn)換功能),如:將二叉樹的順序結(jié)構(gòu)轉(zhuǎn)換為二/三叉鏈表;將序偶的集合結(jié)構(gòu)轉(zhuǎn)換為二/三叉鏈表;
方向3:建立表達(dá)式樹類;
方向4:其他二叉樹類的應(yīng)用算法:計(jì)算寬度、結(jié)點(diǎn)坐標(biāo)…
方向5:趣味賽題的程序結(jié)構(gòu)的共性研究;
2 二叉線索樹類
建立二叉樹線索樹類,應(yīng)包含以下成員函數(shù):構(gòu)造、某種線索化、遍歷。
方向1:三類線索樹、樹類,構(gòu)成類的繼承體系
方向2:線索樹的插入、刪除等算法
3 樹類
采用二叉鏈表結(jié)構(gòu),建立樹類,應(yīng)包含以下成員函數(shù):構(gòu)造、先/后根遍歷、結(jié)點(diǎn)/樹的度、深度。
4 哈夫曼類
采用哈夫曼樹結(jié)構(gòu),計(jì)算哈夫曼編碼 60、;實(shí)現(xiàn)壓縮與解壓縮功能。
第6章 樹
6 線索二叉樹
如何快捷地找出結(jié)點(diǎn)在遍歷過程中的前驅(qū)、后繼?如何保存遍歷過程得到的先后次序?
方法
評(píng)價(jià)
每個(gè)結(jié)點(diǎn)增加前驅(qū)域、后繼域。
浪費(fèi)空間
利用結(jié)點(diǎn)的空鏈域存儲(chǔ)前驅(qū)指針和后繼指針。
問題:如何區(qū)別左孩子指針和前驅(qū)指針?
對(duì)策:增加標(biāo)記域。
質(zhì)疑:浪費(fèi)空間?
解釋:標(biāo)記數(shù)據(jù)有許多靈巧的存儲(chǔ)方法。
6.1 線索二叉樹的存儲(chǔ)結(jié)構(gòu)
中序線索樹示例:
6.2 線索二叉樹類
// 二叉線索樹結(jié)點(diǎn)
template 61、NodeType ltype,rtype;
T data;
BinThrTreeNode 62、er(); // 中序線索化
// 求中序遍歷中的后繼、前驅(qū)
BinThrTreeNode 63、線索化
設(shè)已有二叉樹所有結(jié)點(diǎn)的ltype、rtype為L(zhǎng)INK
①遍歷指針p依次指向各結(jié)點(diǎn),m_lastp緊隨其后,指向上一個(gè)被訪問的結(jié)點(diǎn)。
②每次指針變調(diào)整前,進(jìn)行*p的前驅(qū)線索化、*m_lastp的后繼線索化。
template 64、ss T>
void BinThrTree 65、ype=THREAD; m_lastp->rchild=p; }
m_lastp = p;
// 右子樹中序線索化
if(p->rtype==LINK) MT_DoOrder( p->rchild );
}
時(shí)間/空間復(fù)雜度: 等同于原遍歷算法。
6.3.2 利用非遞歸遍歷程序模式
注意:在先序、中序、后續(xù)線索化中,最后一個(gè)結(jié)點(diǎn)的后繼線索化,需要具體分析。
先序、中序
m_lastp->rtype=THREAD;
后序
if(m_lastp->rchild) m_lastp->rtype=LINK;
else 66、 m_lastp->rtype=THREAD;
6.4 中序線索樹中的若干應(yīng)用
6.4.1 檢索結(jié)點(diǎn)
求*p的后繼
若p->RTag=THREAD:后繼為*p->rchild
若p->RTag=LINK :后繼為*p的右子樹中最左下結(jié)點(diǎn)
求*p的前驅(qū)
若p->LTag=THREAD:前驅(qū)為*p->lchild
若p->LTag=LINK :前驅(qū)為*p的左子樹中最右下結(jié)點(diǎn)
// 求*p的后繼
template
- 溫馨提示:
1: 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
2: 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
3.本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
5. 裝配圖網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 采購管理4
- 手術(shù)室的安全管理教材
- 圖表文轉(zhuǎn)換之徽標(biāo)..課件
- 3.2.1古典概型
- 廣泛的民主權(quán)利 (3)
- 3.3公式法(1)
- 哲學(xué)家和船夫的故事
- 古詩十九首-行行重行行
- 第8章 財(cái)務(wù)報(bào)表
- 戰(zhàn)略性績(jī)效管理篇_方振邦
- 銅梁總規(guī)分析課件
- 1.2有理數(shù) (3)
- 第二章市場(chǎng)經(jīng)濟(jì)體制-第一章政治經(jīng)濟(jì)學(xué)研究對(duì)象與經(jīng)濟(jì)制度
- 彌漫大B細(xì)胞淋巴瘤一線治療新標(biāo)準(zhǔn)課件
- 對(duì)公信貸政策知識(shí)培訓(xùn)