!!!Chapter 4 Trees BST/AVLT

4.1 Preliminaries

A tree is a collection of nodes. The collection can be empty; otherwise, a tree consists of a distinguished node r, called the root, and zero or more nonempty (sub)trees T1, T2,..., T,, each of whose roots are connected by a directed edge from r.

The root of each subtree is said to be a child of r, and r is the parent of each subtree root.

A tree is a collection of N nodes, one of which is the root, and N-1 edges. Each edge connects some node to its parent, every node except the root has one parent.

Node with no children are known as leaves; Node with the same parent are siblings.

A path from node n1 to nk is a sequence of n1, n2, ... , nk such that ni is the parent of n(i+1). Thelength of the path is k-1. There is a path of length zero from every node to itself. In a tree, there is exactly one path from the root to each node.

The depth of ni is the length of the unique path from the root to ni. The depth of the root is 0.

The height of ni is the length of the longest path from ni to a leaf. The height of all leaves is 0. The height of a tree is the height of the root.

If there is a path from n1 to n2, then n1 is the ancestor of n2 and n2 is thedescendant of n1. If n1 ≠ n2, then n1 is a proper ancestor of n2 and n2 is a proper descendant of n1.

4.1.1 Implementation of Trees

Since the number of children per node can vary greatly, we use FirstChild and NextSibling to implement a treeP91:

typedef struct TreeNode *PtrToNode;

struct TreeNode
{
    ElementType Element;
    PtrToNode  FirstChild;
    PtrToNode  NextSibling;
};

4.1.2 Tree Traversals with an Application

We can use tree to implement a directory structure. + two files in different directories can share same name.

Preorder traversal(先序遍历): work at a node is performed before its children are processed:

Routine to list a directory in a hierarchical file sys:

// to print all directory/file in a file sys
static void ListDir( DirectoryOrFile D, int Depth)
{
    if( D is a legitimate entry)
    {
        PrintNmae( D, Depth )
        if( D is a directory )
            for each child, C, of D
                ListDir(C, Depth + 1 )    //recursively list elements
    }
}

void ListDirectory( DirectoryOrFile D)
{
    ListDir( D, 0 );
}
Time Complexity: O(N)
Postorder traversal(后序遍历):the work at a node is performed after its children are evaluated.

Routine to calculate the size of a directory:

static void SizeDirectory( DirectoryOrFile D )
{
    int TotalSize;
    TotalSize = 0;
    if( D is a legitimate entry )
    {
        TotalSize = FileSize( D );
        if( D is a directory )
            for each children, C, of D
                TotalSize += SizeDirectory( C );
    }
    return TotalSize;
} 

4.2 Binary Trees

A binary tree is a tree in which no node can have more than two children.

The depth of an average binary tree is considerably smaller than N.

For binary search tree, the average depth is O(logN)

4.2.1 Implementation

For a binary tree, a node is a structure consisting of the Key information plus two pointers to other nodes.

Binary tree node declarations:

typedef struct TreeNode *PtrToNode;
typedef struct PtrToNode Tree;

Struct TreeNode
{
    ElementType Element;
    Tree             left;
    Tree             right;
};

4.2.2 Expression Tree

Inorder Expression(中序遍历): first produce the left subtree, then the root node, then the right subtree.

Constructing an expression Tree from a postfix expression. P 98

4.3 The Search Tree ADT -- Binary Search Tree(BST)

Binary search tree: for every node, X, in the tree, the values of all the keys in its left subtree are smaller than the key value in X, and the values of all the keys in its right subtree are larger than the key value in X.

Binary search tree declarations:

#ifndef _Tree_H

struct TreeNode;
typedef struct TreeNode *Position;
typedef struct TreeNode *SearchTree;

SearchTree MakeEmpty( SearchTree T );
Position Find( ElementType X, SearchTree T );
Position FindMin( SearchTree T );
Position FindMax( SearchTree T );
SearchTree Insert( ElementType X, SearchTree T );
SearchTree Delete( ElementType X, SearchTree T );
ElementType Retrieve( Position P );

#endif 

//place in implementation file
struct TreeNode
{
    ElementType Element;
    SearchTree Left;
    SearchTree Right;
};
Routine to make an empty tree:

SearchTree MakeEmpty ( SearchTree T )
{
    if( T != NULL )
    {
        MakeEmpty( T->Left );
        MakeEmpty( T-> Right );
        free( T );
    }
}
Routine to Find an element:

Position Find( ElementType X, SearchTree T )
{
//first, we must test for empty case
    if( T == NULL )
        return NULL;
    if( X < T->Element )
        return Find(X, T->Left);
    else if(X > T-> Element)
        return Find(X, T->Right);
    else
        return T;
}
Time Complexity O(logN)
Routine of FindMin/ FindMax:

// Find the left most element:recursively
Position FindMin( SearchTree T )
{
    if( T == NULL )
        return NULL;
    else if( T->Left == NULL)
        return T;
    else
        return FindMin( T->Left );
}

//Find the right most element nonrecursively
Position FindMax( SearchTree T )
{
    if( T != NULL )
        while( T->Right != NULL )
            T = T->Right;
    return T;
}
Insertion routine:

// do insert recursively, everytime will return 
// the root of the new tree
SearchTree Insert( ElementType X, SearchTree T )
{
    if( T == NULL )
    {
        T = malloc( sizeof( struct TreeNode ) );
        if( T == NULL )
            FatalError( "Out of Space" );
        else
        {
            T->Element = X;
            T->Left = T->Right = NULL;
        }
    }
    else if( X < T->Element )
        T->Left = Insert( X, T->Left );
    else if ( X > T->Element )
        T->Right = Insert( X, T->Right );
    /* Else X is in the tree, we do nothing*/
    return T;
} 

4.3.5 Delete

1. if the node is a leaf, it can be deleted immediately

2. if the node has one child, the node can be deleted after its parent adjusts a pointer to bypass the node.

3. If the node has two children, we replace the data of this node with the smallest data of the right subtree and recursively delete that node.

*4. if the number of deletion is small, we can use lazy deletion: when an element is to be deleted, it is left in the tree and merely marked as being deleted. We can also use a count in the struct!

SearchTree Delete( ElementType X, SearchTree T )
{
    Position TmpCell;
    if( T == NULL )
        Error( "Element not found" );
    else if( X < T->Element )
        T->Left = Delete( X, T->Left );
    else if( X > T->Element )
        T->Right = Delete( X, T->Right );
    //Found element to be deleted
    else if( T->Left && T->Right ) //T has two children
    {
        TmpCell = FindMin( T->Right );
        T->Element = TmpCell->Element;
        T->Right = Delete( T->Element, T->Right );
    }
    else
    {
        TmpCell = T;
        if( T->Left == NULL )   //also handles 0 child
            T = T->Right;
        else if( T->Right == NULL )
            T = T->Left;
        free( TmpCell );
    }
    return T;
} 
On average, search/insert/delete only takes O(logN). For worst case, the BST will become a linked list, and search/insert/delete will take O(N)

4.4 AVL Trees

An AVL tree is identical to a binary search tree, except that for every node in the tree, the height of the left and right subtrees can differ by at most 1.

The minimum number of nodes, S(h), in an AVL tree of height h is given by: S(h) = S(h-1) + S(h-2) + 1

For AVL tree, all the tree operations can be performed on O(logN)

After an insertion, only nodes that are on the path from the insertion point to the root might have their balance altered because only those nodes have their subtrees altered.

There are four scenarios that may violate the rule:

1. An insertion into the left subtree of the left child of a,

2. An insertion into the right subtree of the left child of a,

3. An insertion into the left subtree of the right child of a,

4. An insertion into the right subtree of the right child of a,

1 & 4 are mirror image symmetries: single rotation

2 & 3 are mirror image symmetries: double rotation


a is the first node that violate the balance rule!

Wiki: http://en.wikipedia.org/wiki/AVL_tree

AVL Tree Implementation

#ifndef _AvlTree_H

struct AvlNode;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;

AvlTree MakeEmtpy( AvlTree T );
Position Find( ElementType X, AvlTree T );
Position FindMin( AvlTree T );
Position FindMax( AvlTree T );
AvlTree Insert( ElementType X, AvlTree T );
AvlTree Delete( ElementType X, AvlTree T );
ElementType Retrieve( Position P );

#endif

//place in the implementation file
struct AvlNode
{
    ElementType Element;
    AvlTree     Left;
    AvlTree     Right;
    int         Height;
};
Function to return the height(we write this function to avoid testing NULL pointer when need to get the height of a node):

static int Height( Position P )
{
    if( P == NULL )
        return -1;
    else
        return P->Height;
}
Insert into AVL tree:

AvlTree Insert( ElementType X, AvlTree T )
{
    if( T == NULL )
    {
        ...      // allocate space and test
        T->Element = X; T->Height = 0;
        T->Left = T->Right = NULL;
    }
    else if( X < T->Element )
    {
        T->Left = Insert( X, T->Left );
        // if we break the balance rule, the left side must be higher
        if( Height( T->Left ) - Height( T->Right ) == 2 )
            if( X< T->Left->Element )           // case 1
                T = SingleRotateWithLeft( T );
            else                                // case 2
                T = DoubleRotateWithLeft( T );
    }
    else if( X > T->Element )
    {
        T->Right = Insert( X, T->Right );
        if( Height( T->Right ) - Height( T->Left ) == 2 )
            if( X > T->Right->Element )         // case 3
                T = SingleRotateWithRight( T );
            else                                // case 4
                T = DoubleRotateWithRight( T );
    }
    
    T->Height = Max( Height( T->Left ), Height( T->Right ) ) + 1;
    return T;
}
Single rotate (case 1 left-left ):

//K2 has left child
static Position SingleRotateWithLeft( Position K2 )
{
    Position K1;            //K1 is the left child of K2
    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), K2->Height ) + 1;

    Return K1;
}
Double rotate (case 3 left-right)
//K3 has left child, the child has right child
static Position DoubleRotateWithLeft( Position K3 )
{
    K3->Left = SingleRotateWithRight(K3->Left);
    return SingleRotateWithLeft(K3);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要将AVL模块的代码编写完成,并将其保存为一个单独的源文件(例如avl.c和avl.h)。接着,需要在原有的BST模块中包含AVL模块的头文件,并在需要使用AVL树的地方调用AVL模块提供的函数。 具体来说,可以按照以下步骤进行操作: 1. 在原有的BST模块中包含AVL模块的头文件。例如,在bst.h文件中加入以下代码: ``` #include "avl.h" ``` 2. 在BST模块中定义一个AVL树的结构体,并在需要使用AVL树的地方使用该结构体。例如,在bst.c文件中定义一个AVL树的结构体: ``` struct avl_tree { // AVL树的定义 }; void bst_insert(bst_tree* t, int key) { // 在这里使用AVL树的插入函数 } ``` 3. 在AVL模块中实现AVL树的插入函数,并在该函数中使用BST模块中的函数进行插入操作。例如,在avl.c文件中实现AVL树的插入函数: ``` #include "bst.h" void avl_insert(avl_tree* t, int key) { // 在这里使用BST模块中的函数进行插入操作 bst_insert(&t->bst, key); // 在这里进行AVL平衡操作 // ... } ``` 4. 在需要使用AVL树的地方,使用AVL模块提供的函数进行操作。例如,在main函数中使用AVL树: ``` #include "bst.h" #include "avl.h" int main() { avl_tree t; avl_init(&t); avl_insert(&t, 5); avl_insert(&t, 3); avl_insert(&t, 7); // ... return 0; } ``` 通过以上步骤,就可以在原有的BST模块中加入AVL模块的代码,实现横向对比。需要注意的是,在使用AVL树的时候,需要使用AVL模块提供的函数进行操作,而不是直接使用BST模块中的函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值