天天看點

資料結構與算法python第四章_《資料結構與算法分析——c語言描述》第四章筆記與課後練習解答...

關于第四章:

第四章講的是樹,屬于圖類的一個分支。

術在包括查找、編譯器、資料庫方面都有很多很大的用途,也是一類非常基礎的資料結構。

筆記:

一棵樹就是N個節點和N-1條邊的集合。

周遊方式:

(1)先序周遊:父節點在其兒子節點之前處理。

(2)後序周遊:父節點在其兒子節點之後處理。

(3)中序周遊:先周遊左子樹,再周遊根,最後周遊右子樹。

(4)層序周遊:在D深度的節點要在D+1深度的節點之前處理。

二叉樹:

二叉樹是一棵樹,其中每個節點不能有多于兩個兒子。

struct TreeNode {

ElemType Data;

Tree Left;

Tree Right;

};

具有N個節點的每一棵二叉樹都需要N+1個NULL指針。

查找二叉樹:

對于樹中的每個節點X,它的左子樹的關鍵字值少于X的關鍵字值,而它的右子樹的關鍵字值大于X的關鍵字值。

在實作中,要注意的是MakeEmpty和Delete兩個函數:

Tree MakeEmpty(Tree T)

{

if (T != NULL) {

MakeEmpty(T->Left);

MakeEmpty(T->Right);

free(T);

}

return NULL;

}

在MakeEmpty中,後序周遊是必要的,否則實作錯誤。

Position Delete(Tree T,ElemType Search)

{

Position Target;

if (T == NULL)

return NULL;

else if (Search < T->Data)

T->Left = Delete(T->Left,Search);

else if (Search > T->Data)

T->Right = Delete(T->Right,Search);

else {

if (T->Left && T->Right) { // two child

Target = FindMin(T->Right);

T->Data = Target->Data;

T->Right = Delete(T->Right,T->Data);

}

else { // one child

Target = T;

if (T->Left == NULL)

T = T->Right;

else if (T->Right == NULL)

T = T->Left;

free(Target);

}

}

}

而在delete函數中,删除兩兒子節點的時候,先是将T節點和T的右子樹的最小值節點進行了移位,而真正的删除在完成移位之後遞歸進行,是以無須free調用。

而“真正的删除”到one child的情況才進行。

另外因為這種删除方式可能造成樹的不平衡,如果追求平衡,可以用随機删除右節點的最小值或左節點的最大值來保持平衡。

AVL樹:

平衡樹的出現避免了二叉樹深度過深的情況。

AVL樹是其每個節點的左子樹和右子樹的高度最多差1的二叉樹。

AVL樹的難點自然是平衡的保持,這要通過旋轉來達到。

書上AVL章節的前邊部分,描述得比較抽象,看得我一頭霧水,翻到後面看見代碼之後,豁然開朗。

強烈建議不了解的不要死讀描述(像我),直接跳到後面看代碼就好。

Tree Insert(Tree T,ElemType Input)

{

if (T == NULL) {

T = malloc(sizeof(struct TreeNode));

T->Data = Input;

T->Height = 0;

T->Left = T->Right = NULL;

}

else if (Input < T->Data) {

T->Left = Insert(T->Left,Input);

// rotate

if ((Height(T->Left) - Height(T->Right)) == 2)

if (Input < T->Left->Data)

T = SingleLeft(T);

else

T = DoubleLeft(T);

}

else if (Input > T->Data) {

T->Right = Insert(T->Right,Input);

// rotate

if ((Height(T->Right) - Height(T->Left)) == 2)

if (Input > T->Right->Data)

T = SingleRight(T);

else

T = DoubleRight(T);

}

T->Height = Max(Height(T->Left),Height(T->Right)) + 1;

return T;

}

static Tree SingleLeft(Tree K2)

{

Position K1;

K1 = K2->Left;

K2->Left = K1->Right;

K1->Right = K2;

K2->Height = Max(Height(K2->Left),Height(K2->Right)) + 1;

K1->Height = Max(Height(K1->Left),Height(K1->Right)) + 1;

return K1;

}

static Tree DoubleLeft(Tree K3)

{

K3->Left = SingleRight(K3->Left);

return SingleLeft(K3);

}

static Tree SingleRight(Tree K2)

{

Position K1;

K1 = K2->Right;

K2->Right = K1->Left;

K1->Left = K2;

K2->Height = Max(Height(K2->Left),Height(K2->Right)) + 1;

K1->Height = Max(Height(K1->Left),Height(K1->Right)) + 1;

return K1;

}

static Tree DoubleRight(Tree K3)

{

K3->Right = SingleLeft(K3->Right);

return SingleRight(K3);

}

課後練習:

4。8

(a) - * * a b + c d e

(b) ( ( a * b ) * ( c + d ) ) - e

(c) a b * c d + * e -

4。13

(1)c

(2)a

4。15

(a)(a) N (0) = 1, N (1) = 2, N (H ) = N (H −1) + N (H −2) + 1.

這個在書本80頁有談到。

4。20

執行二叉樹式的删除之後,再使用旋轉來保持删除某節點後的平衡,跟插入對比,删除需要更多的旋轉操作。

4。22

這裡答案錯誤了,是return k2才對。

這種操作的清晰性比執行兩個單旋轉來得清晰,我覺得。XD

Position

DoubleRotateWithLeft( Position K3 )

{

Position K1, K2;

K1 = K3->Left;

K2 = K1->Right;

K1->Right = K2->Left;

K3->Left = K2->Right;

K2->Left = K1;

K2->Right = K3;

K1->Height = Max( Height(K1->Left), Height(K1->Right) ) + 1;

K3->Height = Max( Height(K3->Left), Height(K3->Right) ) + 1;

K2->Height = Max( K1->Height, K3->Height ) + 1;

return K2; // 書本的這裡錯了

}

4。32

這題很簡單,可我沒做出來。。。

void

PrintRange( ElementType Lower, ElementType Upper, SearchTree T )

{

if( T != NULL )

{

if( Lower <= T->Element )

PrintRange( Lower, Upper, T->Left );

if( Lower <= T->Element && T->Element <= Upper )

PrintLine( T->Element );

if( Upper >= T->Element )

PrintRange( Lower, Upper, T->Right );

}

}

4。35

這道題使用隊列,對二叉樹實行層序周遊:

将同一深度的節點放到隊列裡(比如深度D),處理完後再Dequeue出去,然後再處理D+1深度的節點。