!!!Chapter 4 Trees BST/AVLT

本文详细介绍了树的基本概念,包括树的定义、节点、路径、深度、高度等,并阐述了树在目录结构、文件系统、二叉树、表达式树、二叉搜索树、AVL树等场景的应用。此外,文章还深入探讨了AVL树的实现及平衡调整机制,为读者提供了一次全面而深入的树结构学习之旅。

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);
}

源码直接下载地址: https://pan.quark.cn/s/d280357b18e5 在网页构建领域中,HTML5被视为当代网页工程的基础规范,其问世显著增强了页面的视觉表现力与用户互动性。本工程致力于运用HTML5技术开发一个电视剧信息展示页面,目的是呈现诸如剧名、演员构成、故事梗概等电视剧关键资料。接下来将深入阐释如何借助HTML5的结构化组件和样式管理功能达成此项目目标。 我们必须掌握HTML5的核心框架。一个规范的HTML5文档一般包含`<!DOCTYPE html>`声明、`<html>`根标记、`<head>`头部标记和`<body>`主体标记。在头部区域,可以配置网页的基本元数据,例如字符集设定、页面标题等。在主体部分,将具体构建电视剧信息列表的内容。 电视剧展示页面通常包含多个条目,每个条目对应一部电视剧。HTML5中的`<section>`标记用于内容模块化,适合表示单个电视剧的详细信息区域。每个`<section>`内部,可使用`<h2>`标题标记显示剧名,`<img>`图像标记插入宣传剧照,`<p>`段落标记呈现剧情介绍,而`<ul>`无序列表与`<li>`列表项标记则用于罗列演员阵容。 为了优化页面布局,需要借助CSS(层叠样式表)进行样式管理。HTML5引入了创新的CSS选择器与布局模型,例如Flexbox和Grid,使页面布局更加灵活多变。在此场景下,可以利用Flexbox为电视剧信息列表实现自适应布局,保障在不同设备尺寸下均能呈现理想视觉效果。具体操作时,可将`<section>`标记设定为Flex容器,通过`display: flex;`属性,并运用`justify-content`和`align-items`属性调整子元素的对...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值