引子:二分查找
int BinarySearch(List tbl,ElementType k)
{
int left,right,mid,NoFound = -1;
left = 0;
right = tbl->length - 1;
while (left <= right) {
mid = (left + right)/2;
if(k < tbl->data[mid])
right = mid - 1;
else if (k > tbl->data[mid])
left = mid + 1;
else
return mid;
}
return NoFound;
}
樹
樹(Tree): n(n>0)個(gè)節(jié)點(diǎn)構(gòu)成的集合呀枢。
當(dāng)n=0時(shí)胚股,為空樹。
對(duì)于一棵非空樹(n>0)裙秋,它具有一下性質(zhì):
- 樹中有一個(gè)稱為“根(Root)”的特殊結(jié)點(diǎn)琅拌,用r表示;
- 其余結(jié)點(diǎn)可分為m(m>0)個(gè)互不相交的有限集T1,T2,..摘刑,Tm财忽,其中每個(gè)集合本身又是一棵樹,稱為原來樹的“子樹(SubTree)'
- n個(gè)節(jié)點(diǎn)的樹有n-1個(gè)邊
樹的一些基本術(shù)語:
- 結(jié)點(diǎn)的度(Degree):結(jié)點(diǎn)的子樹個(gè)數(shù)
- 樹的度:樹的所有結(jié)點(diǎn)中最大的度數(shù)
- 葉結(jié)點(diǎn) (Leaf):度為0的結(jié)點(diǎn)
- 父結(jié)點(diǎn) (Parent):有子樹的結(jié)點(diǎn)是其子樹
的根結(jié)點(diǎn)的父結(jié)點(diǎn) - 子結(jié)點(diǎn) (Child):若A結(jié)點(diǎn)是B結(jié)點(diǎn)的父結(jié)
點(diǎn)泣侮,則稱B結(jié)點(diǎn)是A結(jié)點(diǎn)的子結(jié)點(diǎn);子結(jié)點(diǎn)也稱孩子結(jié)點(diǎn)即彪。 - 兄弟結(jié)點(diǎn)(Sibling):具有同一父結(jié)點(diǎn)的各
結(jié)點(diǎn)彼此是兄弟結(jié)點(diǎn)。 - 路徑和路徑長度:從結(jié)點(diǎn)n,到n的路徑為一
個(gè)結(jié)點(diǎn)序列n1,n2 ,… 活尊,n ,n是 n+的父結(jié)點(diǎn)隶校。路徑所包含邊的個(gè)數(shù)為路徑的長度。 - 祖先結(jié)點(diǎn)(Ancestor):沿樹根到某一結(jié)點(diǎn)路
徑上的所有結(jié)點(diǎn)都是這個(gè)結(jié)點(diǎn)的祖先結(jié)點(diǎn)蛹锰。 - 子孫結(jié)點(diǎn)(Descendant):某一結(jié)點(diǎn)的子樹
中的所有結(jié)點(diǎn)是這個(gè)結(jié)點(diǎn)的子孫深胳。 - 結(jié)點(diǎn)的層次(Level):規(guī)定根結(jié)點(diǎn)在1層,其它任一結(jié)點(diǎn)的層數(shù)是其父結(jié)點(diǎn)的層數(shù)加1铜犬。12.樹的深度(Depth):樹中所有結(jié)點(diǎn)中的最大層次是這棵樹的深度舞终。
二叉樹
二叉樹T: 一個(gè)有窮的結(jié)點(diǎn)集合。
這個(gè)集合可以為空
若不為空癣猾,則它是由根結(jié)點(diǎn)和稱為其左子樹T和右子樹T的兩個(gè)不相交的二叉樹組成敛劝。
1. 特殊的二叉樹
-
斜二叉樹
斜二叉樹 -
滿二叉樹:一棵深度為k且有2n-1個(gè)節(jié)點(diǎn)的二叉樹
滿二叉樹 -
完全二叉樹:一棵深度為k有n個(gè)節(jié)點(diǎn)的二叉樹,當(dāng)且僅當(dāng)每個(gè)節(jié)點(diǎn)的編號(hào)與深度為k的滿二叉樹中的編號(hào)一一對(duì)應(yīng)的二叉樹
完全二叉樹
2. 二叉樹的幾個(gè)性質(zhì)
- 一個(gè)二叉樹第i層最多有2i-1個(gè)結(jié)點(diǎn)(i>=1)
- 深度為k的二叉樹最大結(jié)點(diǎn)數(shù)為2k -1 (k>=1)
- 對(duì)于任何非空二叉樹T, n0表示葉子結(jié)點(diǎn)個(gè)數(shù)纷宇,n2表示度為2的非葉子結(jié)點(diǎn)個(gè)數(shù)夸盟,那么滿足n0 = n2 + 1;
3. 二叉樹的存儲(chǔ)結(jié)構(gòu)
1. 順序存儲(chǔ)結(jié)構(gòu)
但是對(duì)于一般二叉樹來說如果補(bǔ)全空缺位,改成完全二叉樹之后在存儲(chǔ)會(huì)造成空間浪費(fèi)像捶。
2. 鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)
4. 二叉樹的遍歷
1. 先序遍歷
? 1. 訪問根結(jié)點(diǎn)
? 2. 先序訪問左子樹
? 3. 先序訪問右子樹
1. 遞歸遍歷
void PreOrserTraversal(BinTree BT)
{
if(BT){
printf("%d",BT->data);
PreOrserTraversal(BT->left);
PreOrserTraversal(BT->right);
}
}
2. 非遞歸遍歷
void PreOrderTraversal(BinTree BT)
{
BinTree T = BT;
Stack s = CreateStack(MAXSIZE);
while (T || !IsEmpty(s)) {
if(T){
Push(T);
printf("%d",T->data);
T = T->left;
}else{
T = Pop(s);
T -> right;
}
}
}
2. 中序遍歷
? 1. 中序訪問左子樹
? 2. 訪問根結(jié)點(diǎn)
? 3. 中序訪問右子樹
1. 遞歸遍歷
void InOrderTraversal(BinTree BT)
{
if(BT){
InOrderTraversal(BT->left);
printf("%d",BT->data);
InOrderTraversal(BT->right);
}
}
2. 非遞歸遍歷
void InOrderTraversal(BinTree BT)
{
BinTree T = BT;
Stack s = CreateStack(MAXSIZE);
while (T || !IsEmpty(s)) {
if(T){
Push(T);
T = T->left;
}else{
T = Pop(s);
printf("%d",T->data);
T = T->right;
}
}
}
3. 后序遍歷
? 1. 后序訪問左子樹
? 2. 后序訪問右子樹
? 3. 訪問根結(jié)點(diǎn)
1. 遞歸遍歷
void PostOrderTraversal(BinTree BT)
{
if(BT){
PostOrderTraversal(BT->left);
PostOrderTraversal(BT->right);
printf("%d",BT->data);
}
}
2. 非遞歸遍歷
void PostOrderTraversal(BinTree BT)
{
BinTree T = BT;
Stack s = CreateStack(MAXSIZE);
while (T || !IsEmpty(s)) {
if(T){
Push(T);
T = T->left;
}else{
T = Pop(s);
if(T->right == NULL || T->flag == 1){
printf("%d",T->data);
T = NULL;
}else{
Push(T);//重新入棧
T->flag == 1; // flag表示已經(jīng)訪問過右子樹了
T = T->right;
}
}
}
}
4. 層序遍歷
? 1. 先將根結(jié)點(diǎn)入隊(duì)列
? 2. 從隊(duì)列中取出結(jié)點(diǎn)并訪問
? 3. 然后將該結(jié)點(diǎn)的所有非空子結(jié)點(diǎn)入隊(duì)列
void LevelOrderTraversal(Bin?Tree BT)
{
Queue Q;
BinTree T;
if(!BT) return;
Q = CreateQueue(MAXSIZE);
EnQueue(Q, BT);
while (!IsEmpty(Q)) {
T = DeQueue(Q);
printf("%d",T->data);
if(T->left) EnQueue(Q,T->left);
if(T->right) EnQueue(Q, T->right);
}
}
5. 遍歷應(yīng)用例子
1. 遍歷二叉樹中的葉子結(jié)點(diǎn)
void PreOrserTraversal(BinTree BT)
{
if(BT){
if(!BT->left && !BT->right)
printf("%d",BT->data);
PreOrserTraversal(BT->left);
PreOrserTraversal(BT->right);
}
}
- 這個(gè)例子也可以用中序和后序
2. 求二叉樹的高度
int PostOrderGetHeight(BinTree BT)
{
int Max, HL, HR;
if(BT){
HL = PostOrderGetHeight(BT->left);
HR = PostOrderGetHeight(BT->right);
Max = HL > HR ? HL : HR;
return Max + 1;
}
return 0;
}
6. 二叉樹同構(gòu)問題
給定兩棵樹T1和T2上陕。如果T1可以通過若干次左右孩子互換就變成T2桩砰,則我們稱兩棵樹是“同構(gòu)”的。現(xiàn)給定兩棵樹释簿,請(qǐng)你判斷它們是否是同構(gòu)的亚隅。
1. 二叉樹靜態(tài)鏈表表示
2. 建二叉樹
Tree BuildTree(struct TreeNode T[])
{
int N, Root = Null;
char cl,cr;
scanf("請(qǐng)輸入結(jié)點(diǎn)個(gè)數(shù):%d\n",&N);
int *check = (int *)malloc(N*sizeof(int));
if(N){
for (int i = 0; i<N; i++) check[i] = 0;
for (int i = 0; i<N; i++) {
scanf("%c,%c,%c\n",&T[i].Element,&cl,&cr);
if(cl != '-'){
T[i].left = cl - '0';
check[T[i].left] = 1;
}else{
T[i].left = Null;
}
if(cr != '-'){
T[i].right = cr - '0';
check[T[i].right] = 1;
}else{
T[i].right = Null;
}
}
for (int i = 0; i<N; i++) {
if(!check[i]) break;
}
Root = i;
}
return Root; //返回根結(jié)點(diǎn)索引
}
3. 判定同構(gòu)
int Isomorphic(Tree R1,Tree R2)
{
if(R1==Null && R2==Null)
return 1;
if((R1==Null && R2!=Null) || (R1!=Null && R2==Null))
return 0;
if(T1[R1].Element != T2[R2].Element)
return 0;
if(T1[R1].left == Null && T2[R2].left == Null)
return Isomorphic(T1[R1].right,T2[R2].right);
if((T1[R1].left != Null && T2[R2].left != Null)&&
(T1[T1[R1].left].Element == T2[T2[R2].left].Element))
return Isomorphic(T1[R1].left,T2[R2].left)&&Isomorphic(T1[R1].right,T2[R2].right);
else
return Isomorphic(T1[R1].left,T2[R2].right)&&Isomorphic(T1[R1].right,T2[R2].left);
}
二叉搜索樹
二叉搜索樹: 一棵二叉樹,可以為空;如果不為空庶溶,滿足以下性質(zhì):
- 非空左子樹的所有鍵值小于其根結(jié)點(diǎn)的鍵值煮纵。
- 非空右子樹的所有鍵值大于其根結(jié)點(diǎn)的鍵值。
- 左渐尿、右子樹都是二叉搜索樹。
二叉搜索樹操作的特別函數(shù):
- Position Find( ElementType X, BinTree BST):從二叉搜索樹BST中查找元素X矾瑰,返回其所在結(jié)點(diǎn)的地址;
- Position FindMin( BinTree BST):從二叉搜索樹BST中查找并返回最小元素所在結(jié)點(diǎn)的地址;
- Position FindMax( BinTree BST):從二叉搜索樹BST中查找并返回最大元素所在結(jié)點(diǎn)的地址砖茸。
- BinTree Insert(ElementType X, BinTree BST )
- BinTree Delete(ElementType X, BinTree BST )
1. 遞歸查找法
Position Find( ElementType X, BinTree BST)
{
if(!BST) return NULL;
if(X > BST->Data)
return Find(X, BST->Right);
else if(X < BST->Data)
return Find(X, BST->Left);
else
return BST;
}
2. 非遞歸查找法
Position IterFind(ElementType X, BinTree BST)
{
while (BST) {
if(X > BST->Data)
BST = BST->Right;
else if (X < BST->Data)
BST = BST->Left;
else
return BST;
}
return NULL;
}
3. 查找最小元素 遞歸法
Position FindMin( BinTree BST)
{
if(!BST)
return NULL;
else if (!BST->Left)
return BST;
else
return FindMin(BST->Left);
}
4. 查找最大元素 非遞歸法
Position FindMax( BinTree BST)
{
if(BST)
while (BST->Right) {
BST = BST->Right
}
return BST;
}
5. 遞歸插入
BinTree Insert(ElementType X, BinTree BST )
{
if(!BST){
BST = malloc(sizeof(struct TreeNode));
BST->Data = X;
BST->Left = BST->Right = NULL;
}else if(X < BST->Data)
BST->Left = Insert(X, BST->Left);
else if (X > BST->Data)
BST->Right = Insert(X, BST->Right)
return BST;
}
二叉搜索樹的刪除
考慮三種情況:
- 要?jiǎng)h除的結(jié)點(diǎn)是葉子結(jié)點(diǎn):直接刪除,并將父結(jié)點(diǎn)指針置為NULL
- 要?jiǎng)h除的結(jié)點(diǎn)有一個(gè)孩子結(jié)點(diǎn):刪除該結(jié)點(diǎn)殴穴,并將父結(jié)點(diǎn)的指針指向這個(gè)孩子結(jié)點(diǎn)
- 要?jiǎng)h除的結(jié)點(diǎn)有左右子樹:用另一個(gè)結(jié)點(diǎn)替代被刪除的結(jié)點(diǎn):左子樹最大元素或右子樹的最小元素
BinTree Delete(ElementType X, BinTree BST )
{
BinTree Tmp;
if(!BST)
printf("要?jiǎng)h除的元素未找到");
else if(X < BST->Data)
BST->Left = Delete(X, BST->Left);
else if (X > BST->Data)
BST->Right = Delete(X, BST->Right);
else if (BST->Left && BST->Right){
Tmp = FindMin(BST->Right);
BST->Data = Tmp->Data;
BST->Right = Delete(Tmp->Data, BST->Right);
}else{
Tmp = BST;
if(!BST->Left)
BST = BST->Right;
else if (!BST->Right)
BST = BST->Left;
free(Tmp);
}
return BST;
}
平衡二叉樹
- 可以看出(b)的查找效率最高凉夯,越平衡的樹查找效率越高。
平衡因子(Balance Factor 簡(jiǎn)稱:BF): BF = hL - hR , hL hR 分別為左右子樹高度采幌。
平衡二叉樹(AVL樹): 空樹劲够,或者任意左右子樹的高度差絕對(duì)值不超過1,即|BF(T)|<=1休傍。
平衡二叉樹的調(diào)整
1. 判斷二叉樹是否平衡
bool noBalance = false;
struct TreeNode *rjt = NULL;
int checkTreeBalance(struct TreeNode *root)
{
if(NULL == root) return 0;
int l = checkTreeBalance(root->left);
int r = checkTreeBalance(root->right);
if(noBalance) return 0;
int dif = abs(l - r);
if(dif > 1){
noBalance = true;
rjt = root;
}
return l > r ? l + 1 : r + 1;
}
2. 父結(jié)點(diǎn)查詢
TreeNode* queryTopData(TreeNode *root, int data)
{
TreeNode *top = NULL;
TreeNode *tmp = root;
while (tmp != NULL) {
if(data == tmp->data){
return top;
}
top = tmp;
if(data > tmp->data)
tmp = tmp->right;
else if(data < tmp->data)
tmp = tmp->left;
}
return NULL;
}
3. 左左旋轉(zhuǎn)
//不平衡二叉樹
70
/ \
50 80
/ \
40 60
/
30
// 左左旋轉(zhuǎn)后的二叉樹
50
/ \
40 70
/ / \
30 60 80
bool rotateLL(TreeNode **root, TreeNode *notBalanceRoot)
{
if(*root != notBalanceRoot){
printf("左左旋轉(zhuǎn)征绎,非根結(jié)點(diǎn)");
//查找不平衡結(jié)點(diǎn)的父結(jié)點(diǎn)
TreeNode *fNode = queryTopData(*root,notBalanceRoot->data);
if(fNode == NULL) return false;
TreeNode *left = notBalanceRoot->left;
TreeNode *lright = left->right;
left->right = notBalanceRoot;
notBalanceRoot->left = lright;
if(notBalanceRoot == fNode->left)
fNode->left = left;
else if (notBalanceRoot == fNode->right)
fNode->right = left;
return true;
}else{
printf("左左旋轉(zhuǎn),根結(jié)點(diǎn)");
TreeNode *left = notBalanceRoot->left;
TreeNode *lright = left->right;
left->right = notBalanceRoot;
*root->left = lright;
*root = left;
return true;
}
}
4. 左右旋轉(zhuǎn)
//不平衡二叉樹
70
/ \
50 80
/ \
40 60
/
55
//左右旋轉(zhuǎn)后的二叉樹
60
/ \
50 70
/ \ \
40 55 80
bool rotateLR(TreeNode **root, TreeNode *notBalanceRoot)
{
if(*root != notBalanceRoot){
printf("左右旋轉(zhuǎn)磨取,非根結(jié)點(diǎn)");
TreeNode *fNode = queryTopData(*root,notBalanceRoot->data);
if(fNode == NULL) return false;
TreeNode *left = notBalanceRoot->left;
TreeNode *lright = left->right;
TreeNode *lrightLeft = lright->left;
TreeNode *lrightRight = lright->right;
lright->left = left;
lright->right = notBalanceRoot;
left->right = lrightLeft;
notBalanceRoot->left = lrightRight;
if(notBalanceRoot == fNode->left)
fNode->left = lright;
else if (notBalanceRoot == fNode->right)
fNode->right = lright;
return true;
}else{
printf("左右旋轉(zhuǎn)人柿,根結(jié)點(diǎn)");
TreeNode *left = notBalanceRoot->left;
TreeNode *lright = left->right;
TreeNode *lrightLeft = lright->left;
TreeNode *lrightRight = lright->right;
lright->left = left;
lright->right = notBalanceRoot;
left->right = lrightLeft;
notBalanceRoot->left = lrightRight;
*root = lright;
return true;
}
5. 右右旋轉(zhuǎn)
//不平衡的二叉樹
70
/ \
50 80
/ \
75 88
/
85
//右右旋轉(zhuǎn)的二叉樹
80
/ \
70 88
/ \ /
50 75 85
bool rotateRR(TreeNode **root, TreeNode *notBalanceRoot)
{
if(*root != notBalanceRoot){
printf("右右旋轉(zhuǎn),非根結(jié)點(diǎn)");
TreeNode *fNode = queryTopData(*root,notBalanceRoot->data);
if(fNode == NULL) return false;
TreeNode *right = notBalanceRooot->right;
TreeNode *rleft = right->left;
right->left = notBalanceRoot
notBalanceRoot->right = rleft;
if(notBalanceRoot == fNode->left)
fNode->left = right;
else if(notBalanceRoot == fNode->right)
fNode->right = right;
return true;
}else{
printf("右右旋轉(zhuǎn)忙厌,根結(jié)點(diǎn)");
TreeNode *right = notBalanceRooot->right;
TreeNode *rleft = right->left;
right->left = notBalanceRoot
notBalanceRoot->right = rleft;
*root = right;
return true;
}
}
6. 右左旋轉(zhuǎn)
//不平衡二叉樹
70
/ \
50 80
/ \
75 88
\
77
//右左旋轉(zhuǎn)后的二叉樹
75
/ \
70 80
/ / \
50 77 88
bool rotateRL(TreeNode **root,TreeNode *notBalanceRoot)
{
if(*root != notBalanceRoot){
printf("右左旋轉(zhuǎn)凫岖,非根結(jié)點(diǎn)");
TreeNode *fNode = queryTopData(*root,notBalanceRoot->data);
if(fNode == NULL) return false;
TreeNode *right = notBalanceRoot->right;
TreeNode *rleft = right->left;
TreeNode *rleftLeft = rleft->left;
TreeNode *rleftRight = rleft->right;
rleft->left = notBalanceRoot;
rleft->right = right;
notBalanceRoot->right = rleftLeft;
right->left = rleftRight;
if(notBalanceRoot == fNode->left)
fNode->left = rleft;
else if (notBalanceRoot == fNode->right)
fNode->right = rleft;
return true;
}else{
printf("右左旋轉(zhuǎn),根結(jié)點(diǎn)");
TreeNode *right = notBalanceRoot->right;
TreeNode *rleft = right->left;
TreeNode *rleftLeft = rleft->left;
TreeNode *rleftRight = rleft->right;
rleft->left = notBalanceRoot;
rleft->right = right;
notBalanceRoot->right = rleftLeft;
right->left = rleftRight;
*root = rleft;
return true;
}
}