天天看点

数据结构与算法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深度的节点。