【数据结构】第四章:树

1.静态查找

0位置为哨兵

typedef struct LNode *List;
struct LNode {
    ElementType
    Element [MAXSIZE] ;
    int Length;
};
int SequentialSearch(List Tbl,ElementType K)
{                 /*在表Element[1]~Element[n]中查找关键字为K的数据元素*/
    int i;
    Tbl -> Element[0] = K;
    for(i = Tbl->Length; Tbl->Element[i] != K;  i--)
       return i;   /*查找成功返回所在单元下标;不成功返回0*/

}

2.二分查找

int BinarySearch(List Tbl, ElementType K)
{ 
    *在表Tbl中查找大健了为K的数据元素*1
    int left, right, mid, NoFound=-1;
    left= 1; 
    right = Tbl->Length;
    while (left <= right )
    {
        mid = (left+right) / 2;计算中间元素坐标*/
        if( K < Tbl->Element[mid]) right = mid-1; 1*调 整右边界*/
        else if( K > Tbl->Element[mid) left = mid+1;调 整左边界*/
        else return mid; "查找成功, 返回数据元素的下标*1
}
    return NotFound;
}

ASL,是查找算法的查找成功时的平均查找长度的缩写

                              

树的定义:

                                        

子树是不相交的;
除了根结点外,每个结点有且仅有一个父结点;
一棵N个结点的树有N-1条边。
 

树的一些基本术语
1.结点的度(Degree) :结点的子树个数
2. 树的度:树的所有结点中最大的度数
3.叶结点(Leaf) :度为0的结点
4.父结点(Parent) :有子树的结点是其子树的根结点的父结点
5. 子结点(Child) :若A结点是B结点的父结点,则称B结点是A结点的子结点:子结点也,称孩子结点。
6. 兄弟结点(Sibling) :具有同一父结点的各结点彼此是兄弟结点。
7.路径和路径长度: 从结点n到n的路径为- -个结点序列n, n2... nk, n是n4+1的父结点。路径所包含边的个数为路径的长度。
9.祖先结点(Ancestor):沿树根到某一结点路径,上的所有结点都是这个结点的祖先结点。
10.子孙结点(Descendant):某- - 结点的子树中的所有结点是这个结点的子孙。
11.结点的层次(Level) :规定根结点在1层,其它任一结点的层数是其父结点的层数加1。.
12.树的深度(Depth) :树中所有结点中的最;大层次是这棵树的深度。,
 

 

二叉树的定义
二叉树T: 一个有穷的结点集合。
这个集合可以为空
若不为空,则它是由根结点和称为其左子树T[和右子树TR的
两个不相交的二叉树组成。
             

树的存储结构

typedef struct TreeNode *BinTree;
typedef BinTree Position;
struct TreeNode{
    ElementType Data;
    BinTree Left; ;
    BinTree Right;
}

二叉树的遍历
(1)先序遍历
遍历过程为:
①访问根结点;
②先序遍历其左子树;
③先序遍历其右子树。
 

void PreOrderTraversal ( BinTree BT )
{
    if(BT){
        printf ("%d", BT->Data) ;
        PreOrderTraversal( BT->Left ) ;
        PreOrderTraversal( BT->Right ) ;
    }
}

(2)中序遍历
遍历过程为:
①中序遍历其左子树;
②访问根结点;
③中序遍历其右子树。
 

(3)后序遍历
遍历过程为:
①后序遍历其左子树;
②后序遍历其右子树;
③访问根结点。
 

 

二叉树的非递归遍历
*中序遍历非递归遍历算法
非递归算法实现的基本思路:使用堆栈
 

中序遍历非递归遍历算法
➢遇到一个结点,就把它压栈,并去遍历它的左子树;
当左子树遍历结束后,从栈项弹出这个结点并访问它;
➢然后按其右指针再去中序遍历该结点的右子树

void InOrderTraversal ( BinTree BT )
{ 
    BinTree T = BT ;
    Stack S = CreatStack( MaxSize ); /*创建 并初始化堆栈S*/.
    while( T | ! IsEmpty(S) ) {  //树不空或堆栈不空
        while(T){         /*一直向左 并将沿途结点压入堆栈*/
            Push(S,T) ;       //第一次碰到结点,先序遍历非递归只需把Print移动到此处即可
            T = T->Left;
        }
        if(!IsEmpty(S)) { 
            T = Pop(S);         /*第二次碰到节点,结点弹出堆栈*/
            printf("%5d", T->Data);        /* (访问)打印结点*/
            T = T->Right;         /*转向右子树*/
        }
    }
}

层序基本过程:先根结点入队,然后:
①从队列中取出一个元素;
②访问该元素所指结点;
③若该元素所指结点的左、右孩子结点非空,
则将其左、右孩子的指针顺序入队。
 

void LevelOrderTraversal ( BinTree BT )
{
    QueueQ;
    BinTree T; 
    if ( !BT ) return; /*若是空树则直接返回*/
    Q = CreatQueue( MaxSize ); /*创建并初始化队列Q*/
    AddQ(Q,BT);
    while ( !IsEmptyQ( Q ) ) {
        T = DeleteQ(Q ) ;
        printf ("&d\n", T->Data); /*访 问取出队列的结点*/
        if ( T->Left )
            AddQ( Q,T->Left ) ;
        if ( T->Right ) 
            AddQ( Q,T->Right ) ;
    }
}

什么是二叉搜索树

二叉 搜索树,(BST, Binary Search Tree),
也称二叉排序树或二叉查找树
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:
1.非空左子树的所有键值小于其根结点的键值。
2.非空右子树的所有键值大于其根结点的键值。
3.左、右子树都是二叉搜索树。

二叉搜索树的查找操作Find
 

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 /* X == BST->Data */
        return BST; /*查找成功,返回结点的找到结点的地址*/
}

由于非递归函数的执行效率高,
可将“尾递归”函数改为迭代函数
 

Position IterFind( ElementType X,BinTree BST )
{
    while( BST ) {
        if( X > BST->Data )
            BST = BST->Right; /*向右子树中移动,继续查找*/
        else if( X < BST->Data )
            BST = BST->Left; 7*向左子树中移动, 继续查找*/
        else /* X == BST->Data */
            return BST; /*查找成功,返回结点的找到结点的地址*/
    }
    return NULL; /*查找失败*/
}

二叉搜索树的插入
[分析]关键是要找到元素应该插入的位置,
可以采用与Find类似的方法
 

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) ;
                                /*递归插入右子树*/ .
    /* else x已经存在,什么都不做*/
    return BST ;
}

二叉搜索树的删除

考虑三种情况:
1.要删除的是叶结点:直接删除,并再修改其父结点指针---置为NULL
2.要删除的结点只有一个孩子结点  :  将其父结点的指针指向要删除结点的孩子结点
3.要删除的结点有左、右两棵子树   :   用另一结点替代被删除结点:右子树的最小元素或者左子树的最大元素

       1、取右子树中的最小元素替代
       2、取左子树中的最大元素替代
 

BinTree Delete ( ElementType X,BinTree BST ){
    Position Tmp ; 
    if( !BST ) printf ("要删除的元素未找到") ;
    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( BST->Data, BST->Right) ;
            /*在删除结点的右子树中删除最小元素*/
        } else { /*被删除结点有一个或无子结点*/
            Tmp = BST;
            if( !BST->Left ) /* 有右孩子或无子结点*/
                BST = BST->Right;
            else if( !BST->Right ) /*有左孩子或无子结点*/
                BST = BST->Left;
            free( Tmp ) ;
    }
    return BST ;
}

什么是平衡二叉树
“平衡因子( Balance Factor,简称BF) : BF(T) = hu-hr,
其中hu和hR分别为T的左、右子树的高度。
平衡二叉树( Balanced Binary Tree) ( AVL树)
空树,或者任一结点左、右子树高度差的绝对值不超过1,即|BF(T)| <= 1
                                                       

                                   

平衡二叉树的调整

typedef struct AVLNode *Position;
typedef Position AVLTree; /* AVL树类型 */
struct AVLNode{
    ElementType Data; /* 结点数据 */
    AVLTree Left;     /* 指向左子树 */
    AVLTree Right;    /* 指向右子树 */
    int Height;       /* 树高 */
};
 
int Max ( int a, int b )
{
    return a > b ? a : b;
}
 
AVLTree SingleLeftRotation ( AVLTree A )
{ /* 注意:A必须有一个左子结点B */
  /* 将A与B做左单旋,更新A与B的高度,返回新的根结点B */     
 
    AVLTree B = A->Left;
    A->Left = B->Right;
    B->Right = A;
    A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
    B->Height = Max( GetHeight(B->Left), A->Height ) + 1;
  
    return B;
}
 
AVLTree DoubleLeftRightRotation ( AVLTree A )
{ /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
  /* 将A、B与C做两次单旋,返回新的根结点C */
     
    /* 将B与C做右单旋,C被返回 */
    A->Left = SingleRightRotation(A->Left);
    /* 将A与C做左单旋,C被返回 */
    return SingleLeftRotation(A);
}
 
/*************************************/
/* 对称的右单旋与右-左双旋请自己实现 */
/*************************************/
 
AVLTree Insert( AVLTree T, ElementType X )
{ /* 将X插入AVL树T中,并且返回调整后的AVL树 */
    if ( !T ) { /* 若插入空树,则新建包含一个结点的树 */
        T = (AVLTree)malloc(sizeof(struct AVLNode));
        T->Data = X;
        T->Height = 0;
        T->Left = T->Right = NULL;
    } /* if (插入空树) 结束 */
 
    else if ( X < T->Data ) {
        /* 插入T的左子树 */
        T->Left = Insert( T->Left, X);
        /* 如果需要左旋 */
        if ( GetHeight(T->Left)-GetHeight(T->Right) == 2 )
            if ( X < T->Left->Data ) 
               T = SingleLeftRotation(T);      /* 左单旋 */
            else 
               T = DoubleLeftRightRotation(T); /* 左-右双旋 */
    } /* else if (插入左子树) 结束 */
     
    else if ( X > T->Data ) {
        /* 插入T的右子树 */
        T->Right = Insert( T->Right, X );
        /* 如果需要右旋 */
        if ( GetHeight(T->Left)-GetHeight(T->Right) == -2 )
            if ( X > T->Right->Data ) 
               T = SingleRightRotation(T);     /* 右单旋 */
            else 
               T = DoubleRightLeftRotation(T); /* 右-左双旋 */
    } /* else if (插入右子树) 结束 */
 
    /* else X == T->Data,无须插入 */
 
    /* 别忘了更新树高 */
    T->Height = Max( GetHeight(T->Left), GetHeight(T->Right) ) + 1;
     
    return T;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值