数据结构复习(五)——树和二叉树

一、二叉树

1. 二叉树的顺序存储
#include<stdio.h>
#define MaxSize 10
struct TreeNode
{
    ElemType value; //结点中的数据元素
    bool isEmpty;   //结点是否为空
};

//定义一个长度为MaxSize的数组t,按照从上到下、
//从左到右的顺序依次存储完全二叉树中的各个结点
TreeNode t[MaxSize];

//二叉树顺序存储,根节点从1开始编号
//i的左孩子---2i
//i的右孩子---2i+1
//i的父节点---i/2向下取整

//初始化时所有结点标记为空
for(int i=0;i<MaxSize;i++)
{
    t[i].isEmpty=true;
}
2. 二叉树的链式存储
//二叉树的结点(链式存储)--二叉链表
struct ElemType
{
    int value;
};

typedef struct BiTNode
{
    ELemType data;                   //数据域
    struct BiTNode *lchild, *rchild; //左右孩子指针
} BiTNode, *BiTree;

//n个结点的二叉链表共有n+1个空链域
//n个结点,一共有2n个指针,
//除了根结点,每个结点的头上都有一个天线,共有n-1个指针非空
//2n-(n-1)==n+1个空链域

//定义一颗空树
BiTree root = NULL;

//插入根结点1
root = (BiTree)malloc(sizeof(BiTNode));
root->data = {1};
root->lchild = NULL;
root->rchild = NULL;

//插入新结点2
BiTNode *p2 = (BiTNode *)malloc(sizeof(BiTNode));
p2->data = {2};
p2->lchild = NULL;
p2->rchild = NULL;
root->lchild = p2; //作为根节点1的左孩子

//插入新结点3
BiTNode *p3 = (BiTNode *)malloc(sizeof(BiTNode));
p3->data = {3};
p3->lchild = NULL;
p3->rchild = NULL;
root->rchild = p3; //作为根节点1的右孩子

//插入新结点4
BiTNode *p4 = (BiTNode *)malloc(sizeof(BiTNode));
p4->data = {4};
p4->lchild = NULL;
p4->rchild = NULL;
p2->rchild = p4; //作为节点2的右孩子

//插入新结点6
BiTNode *p6 = (BiTNode *)malloc(sizeof(BiTNode));
p6->data = {6};
p6->lchild = NULL;
p6->rchild = NULL;
p3->lchild = p6; //作为节点3的左孩子

//插入新结点7
BiTNode *p7 = (BiTNode *)malloc(sizeof(BiTNode));
p7->data = {7};
p7->lchild = NULL;
p7->rchild = NULL;
p3->rchild = p7; //作为节点3的右孩子

//插入新结点11
BiTNode *p11 = (BiTNode *)malloc(sizeof(BiTNode));
p11->data = {11};
p11->lchild = NULL;
p11->rchild = NULL;
p4->rchild = p11 //作为节点4的右孩子

//插入新结点12
BiTNode *p12 = (BiTNode *)malloc(sizeof(BiTNode));
p12->data = {12};
p12->lchild = NULL;
p12->rchild = NULL;
p6->lchild = p12; //作为节点6的左孩子
3. 二叉树的链式存储——三叉链表
//二叉树的结点(链式存储)--二叉链表
struct ElemType
{
    int value;
};
//二叉树的结点(链式存储)--三叉链表
typedef struct BiTNode
{
    ELemType data;                  //数据域
    struct BiTNode *lchild,*rchild; //左右孩子指针
    struct BiTNode *parent;         //父结点指针
}BiTNode,*BiTree;

//定义一颗空树
BiTree root = NULL;

//插入根结点1
root = (BiTree)malloc(sizeof(BiTNode));
root->data={1};
root->lchild=NULL;
root->rchild=NULL;

//插入新结点2
BiTNode *p2 = (BiTNode *)malloc(sizeof(BiTNode));
p2->data={2};
p2->lchild=NULL;
p2->rchild=NULL;
root->lchild=p2; //作为根节点1的左孩子

//插入新结点3
BiTNode *p3 = (BiTNode *)malloc(sizeof(BiTNode));
p3->data={3};
p3->lchild=NULL;
p3->rchild=NULL;
root->rchild=p3; //作为根节点1的右孩子

//插入新结点4
BiTNode *p4 = (BiTNode *)malloc(sizeof(BiTNode));
p4->data={4};
p4->lchild=NULL;
p4->rchild=NULL;
p2->rchild=p4; //作为节点2的右孩子

//插入新结点6
BiTNode *p6 = (BiTNode *)malloc(sizeof(BiTNode));
p6->data={6};
p6->lchild=NULL;
p6->rchild=NULL;
p3->lchild=p6; //作为节点3的左孩子

//插入新结点7
BiTNode *p7 = (BiTNode *)malloc(sizeof(BiTNode));
p7->data={7};
p7->lchild=NULL;
p7->rchild=NULL;
p3->rchild=p7; //作为节点3的右孩子

//插入新结点11
BiTNode *p11 = (BiTNode *)malloc(sizeof(BiTNode));
p11->data={11};
p11->lchild=NULL;
p11->rchild=NULL;
p4->rchild=p11  //作为节点4的右孩子

//插入新结点12
BiTNode *p12 = (BiTNode *)malloc(sizeof(BiTNode));
p12->data={12};
p12->lchild=NULL;
p12->rchild=NULL;
p6->lchild=p12; //作为节点6的左孩子
4. 二叉树的遍历
typedef struct BiTNode
{
    int data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//访问节点
void visit(BiTNode *)
{
    printf("%d\t",T->data);
}

//先序遍历(根左右)
void PreOrder(BiTree T)
{
    if(T!=NULL)
    {
        visit(T);               //访问根节点
        PreOrder(T->lchild);    //递归遍历左子树
        PreOrder(T->rchild);    //递归遍历右子树
    }
}

//中序遍历(左根右)
void InOrder(BiTree T)
{
    if(T!=NULL)
    {
        InOrder(T->lchild);    //递归遍历左子树
        visit(T);              //访问根节点
        InOrder(T->rchild);    //递归遍历右子树
    }
}

//后序遍历(左右根)
void PostOrder(BiTree T)
{
    if(T!=NULL)
    {
        PostOrder(T->lchild);   //递归遍历左子树
        PostOrder(T->rchild);   //递归遍历右子树
        visit(T);               //访问根节点
    }
}
5. 求树的深度
//求树的深度
int treeDepth(BiTree T)
{
    if(T==NULL)
    {
        return 0;
    }    
    else
    {
        int l=treeDepth(T->lchild);
        int r=treeDepth(T->rchild);
        //数的深度=Max(左子树深度,右子树深度)+1
        return l>r? l+1: r+1;
    }
}
6. 二叉树的层次遍历
算法思想:
①初始化一个辅助队列
②根节点入队
③若队列非空,则队头节点入队,访问该结点,并将其左右孩子插入队尾(如果有的话)
④重复③直至队列为空
//二叉树的结点(链式存储)
typedef struct BiTNode
{
    char data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//链式队列结点
typedef struct LinkNode
{
    BiTNode *data;
    struct LinkNode *next;
}LinkNode;

typedef struct
{
    LinkNode *front,*rear;  //队头队尾指针
}LinkQueue;

//层序遍历
void LevelOrder(BiTree T)
{
    LinkQueue Q;
    InitQueue(Q);   //初始化辅助队列
    BiTree p;  
    EnQueue(Q,T);   //根结点入队
    while(isEmpty(Q)!=true)     //队列不空则循环
    {
        DeQueue(Q,p);   //队头节点出队
        visit(p);       //访问出队结点
        if(p->lchild!=NULL)
            EnQueue(Q,p->lchild);   //左孩子入队
        if(p->rchild!=NULL)
            EnQueue(Q,p->rchild);   //右孩子入队
    }
}

二、线索二叉树

1. 存储和遍历算法
//二叉树的结点(链式存储)
typedef struct BiTNode
{
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//线索二叉树结点(链式存储)
typedef struct ThreadNode
{
    ElemType data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;      //左、右线索标志
                        //tag==0,指针指向孩子
                        //tag==1,指针指向"线索"
}

//中序遍历
void InOrder(BiTree T)  //改名findPre
{
    if(T!=NULL)
    {
        InOrder(T->lchild);     //递归遍历左子树
        visit(T);               //访问根节点
        InOrder(T->rchild);     //递归遍历右子树
    }
}

//访问结点
void visit(BiTNode *q)
{
    if(q==p)    //当前访问结点刚好是结点p
        final = pre;    //找到p的前驱
    else
        pre=q;  //pre指向当前访问的节点
}

//辅助全局变量,用于查找结点p的前驱
BiTNode *p;             //p指向目标结点
BiTNode *pre = NULL;    //指向当前访问结点的中序索引前驱
BiTNode *final = NULL;  //用于记录最终结果
2. 中序线索化二叉树
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左、右线索标志
}ThreadNode,*ThreadTree;

//全局遍历pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;

//中序线索化二叉树T
void CreatInThread(ThreadTree T)
{
    pre = NULL;     //pre初始为NULL
    if(T!=NULL)     //非空二叉树才能线索化
    {
        InThread(T);    //中序线索化二叉树T
        if(pre->rchild==NULL)
            pre->rtag=1; //处理遍历的最后一个结点
    }
}

//中序遍历二叉树,一边遍历一边线索化
void InThread(ThreadTree T)
{
    if(T!=NULL)
    {
        InThread(T->lchild);    //中序遍历左子树
        visit(T);               //访问根节点
        InThread(T->rchild);    //中序遍历右子树
    }
}

void visit(ThreadNode *q)
{
    if(q->lchild==NULL) //左子树为空,建立前驱线索
    {
        q->lchild=pre;
        q->ltag=1;
    }
    if(pre!=NULL && pre->rchild==NULL)
    {
        pre->rchild=q;  //建立前驱结点的后继线索
        pre->rtag=1;    
    }
    pre=q;
}
3. 先序线索化二叉树
#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;

//先序线索化二叉树T
void CreatPreThread(ThreadTree T)
{
    pre = NULL;         //pre初始化为NULL
    if(T!=NULL)         //非空二叉树才能线索化
    {
        PreThread(T);   //先序线索化二叉树
        if(pre->rchild==NULL)
            pre->rtag=1; //处理遍历后的最后一个结点
    }
}

//先序遍历二叉树,一边遍历一边线索化
void PreThread(ThreadTree T)
{
    if(T!=NULL)
    {
        visit(T);                //先处理根节点
        if(T->ltag==0)  //lchild不是前驱线索时才线索化
            PreThread(T->lchild);    //先序遍历左子树
        PreThread(T->rchild);    //先序遍历右子树
    }
}

void visit(ThreadNode *q)       //访问结点
{
    if(q->lchild==NULL) //左子树为空,建立前驱线索
    {
        q->lchild=pre;
        q->ltag=1;
    }
    if(pre!=NULL && pre->rchild==NULL)
    {
        pre->rchild=q;  //建立前驱结点的后继线索
        pre->rtag=1;
    }
    pre=q;
}
4. 后续线索化二叉树
#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;

//后序线索化二叉树T
void CreatPosthread(ThreadTree T)
{
    pre = NULL;         //pre初始化为NULL
    if(T!=NULL)         //非空二叉树才能线索化
    {
        PostThread(T);   //后序线索化二叉树
        if(pre->rchild==NULL)
            pre->rtag=1; //处理遍历后的最后一个结点
    }
}

//后序遍历二叉树,一边遍历一边线索化
void PostThread(ThreadTree T)
{
    if(T!=NULL)
    {
        PreThread(T->lchild);    //后序遍历左子树
        PreThread(T->rchild);    //后序遍历右子树
        visit(T);                //访问根节点
    }
}

void visit(ThreadNode *q)       //访问结点
{
    if(q->lchild==NULL) //左子树为空,建立前驱线索
    {
        q->lchild=pre;
        q->ltag=1;
    }
    if(pre!=NULL && pre->rchild==NULL)
    {
        pre->rchild=q;  //建立前驱结点的后继线索
        pre->rtag=1;
    }
    pre=q;
}
5. 中序线索二叉树找中序后继
#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

//找到以P为根的子树中,第一个被中序遍历的结点
ThreadNode *FirstNode(ThreadNode *p)
{
    //循环找到最左下结点(不一定是叶节点)
    while(p->ltag==0)
        p=p->rchild;
    return p;
}

//在中序线索二叉树中找到结点P的后继结点
ThreadNode *NextNode(ThreadNode *p)
{
    //右子树中最左下结点
    if(p->rtag==0)
        return FirstNode(p->rchild);
    else
        return p->rchild;   //rtag==1直接返回后继线索
}

//对中序线索二叉树进行中序遍历(利用线索实现的非递归算法)
void InOrder(ThreadNode *T)
{
    for(ThreadNode *p = FirstNode(T);p!=NULL;p=NextNode(p))
        visit(p);
}
6. 中序线索二叉树找中序前驱
#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

//在中序线索二叉树中找到指定结点*p的中序前驱pre
//1.若p->ltag==1,则pre=p->lchild
//2.若p->ltag==0,找到左子树中,最后一个被中序遍历的结点 

//找到以P为根的子树中,最后一个被中序遍历的结点
ThreadNode *LastNode(ThreadNode *p)
{
    //循环找到最右下结点(不一定是叶节点)
    while(p->rtag==0)
        p=p->rchild;
    return p;
}

//在中序线索二叉树中找到结点p的前驱结点
ThreadNode *PreNode(ThreadNode *p)
{
    //左子树中最右下结点
    if(p->ltag==0)
        return LastNode(p->lchild);
    else
        return p->lchild;
}

//对中序线索二叉树进行逆向中序遍历
void RevInOrder(ThreadNode *T)
{
    for(ThreadNode *p=LastNode(T);p!=NULL;p=PreNode(p))
        visit(p);
}
7. 先序线索二叉树找先序后继
#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

//在先序线索二叉树中找到指定结点*p的先序后继next
//若p->rtag==1,则next=p->rchild
//若p->rtag==0:肯定有右孩子
//    1.有左孩子:根 左(根 左 右) 右 先序后继则为左孩子
//    2.没有左孩子: 根 右(根 左 右)  先序后继则为右孩子
ThreadNode *NextNode(ThreadNode *p)
{
    if(p->rtag==1)
        return p->rchild;
    else
    {
        if(p->ltag==0)          //有左孩子:根 左(根 左 右) 右 先序后继则为左孩子
            return p->lchild;
        else                    //没有左孩子: 根 右(根 左 右)  先序后继则为右孩子
            return p->rchild;
    }
    
}
7. 先序线索二叉树找先序前驱
先序遍历中,左右子树中的结点只可能是根的后继,不可能是前驱.

//除非从头开始先序遍历

#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

//在先序线索二叉树中找到指定结点*p的先序前驱pre
//若p->ltag==1,则Pre=p->lchild
//若p->ltag==0:肯定有左孩子
8. 后序线索二叉树找后序前驱
在后序线索二叉树中找到指定结点*p的后续前驱Pre
若p->ltag==1,则Pre=p->lchild
若p->ltag==0:肯定有左孩子
1.有右孩子:左 右(左 右 根) 根 后续前驱Pre则为右孩子
2.没有右孩子:左(左 右 根) 根 后序前驱Pre则为左孩子
#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

ThreadNode *NextNode(ThreadNode *p)
{
    if(p->rtag==1)
        return p->rchild;
    else
    {
        if(p->ltag==0)          //有左孩子:根 左(根 左 右) 右 先序后继则为左孩子
            return p->lchild;
        else                    //没有左孩子: 根 右(根 左 右)  先序后继则为右孩子
            return p->rchild;
    }
    
}
9. 后序线索二叉树找后序后继
在后序线索二叉树中找到指定结点*p的后续后继next
若p->rtag==1,则next=p->rchild
若p->rtag==0:肯定有右孩子
后续遍历中,左右子树中的结点只可能是根的前驱,不可能是后继
除非用后续遍历
#include<stdio.h>
//线索二叉树结点
typedef struct ThreadNode
{
    char data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;  //左右线索标志
}ThreadNode,*ThreadTree;

三、树的表示法

1. 双亲表示法(顺序存储)
#include<stdio.h>
#define Max_Tree_Size 100   //树中最多节点数
typedef struct PTNode   //树的结点定义
{               
    char data;          //数据元素
    int parent;         //双亲位置域
}PTNode;
typedef struct PTree    //树的类型定义
{
    PTNode nodes [Max_Tree_Size];   //双亲表示
    int n;              //结点数
}PTree;
2. 孩子表示法
#include<stdio.h>
#define Max_Tree_Size 100   //树中最多节点数
typedef struct CTNode
{
    int child;      //孩子结点在数组中的位置
    struct CTNode *next;    //下一个孩子
}CTNode;
typedef struct CTBox
{
    char data;
    struct CTNode *firstNode;   //第一个孩子
}CTBox;

typedef struct CTree
{
    CTBox nodes[Max_Tree_Size];
    int n,r;    //结点数和根的位置
}CTree;

3. 孩子兄弟表示法
//二叉树的结点(链式存储)
typedef struct BiTNode
{
    char data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//树的存储--孩子兄弟表示法
typedef struct CSNode
{
    char data;
    struct CSNode *firstchild,*nextsibling; //第一个孩子和右兄弟指针
}CSNode,*CSTree;

四、树的遍历

1. 树的先根遍历
树的先根遍历
若树非空,先访问根结点
再依次对每棵子树进行先根遍历
//树的先根遍历 (深度优先遍历)
void PreOrder(TreeNode *R)
{
    if(R!=NULL)
    {
        visit(R);   //访问根结点
        while(R还有下一个子树T)
        {
            PreOrder(T);    //先根遍历下一棵子树
        }
    }
}
//树的先根遍历序列与这棵树相对应的二叉树的先序序列相同。
2. 树的后根遍历.
//若树非空,依次对每棵子树进行后根遍历,最后再访问根结点
void PostOrder(TreeNode *R)
{
    if(R!=NULL)
    {
        while(R还有下一个子树T)
        {
            PostOrder(T);   //后根遍历下一棵子树
            visit(R);       //访问根结点
        }
    }
}
//树的后根遍历序列与这棵树相对应的二叉树的中序序列相同。

五、森林的遍历

先序遍历森林和依次对各个树进行先根遍历效果相同。
先序遍历森林效果等同于依次对二叉树的先序遍历。
森林的中序遍历,效果上等同于依次对各个树进行后根遍历。
中序遍历森林的效果等同于依次对二叉树的中序遍历
树 森林 二叉树
先根遍历 先序遍历 先序遍历
后根遍历 中序遍历 中序遍历

六、二叉排序树

左子树结点值 < 根节点值 < 右子树结点值
进行中序遍历,可以得到一个递增的有序序列
1. 存储
//二叉排序树结点
typedef struct BSTNode
{
    int key;
    struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
2. 在二叉排序树中查找值为key的结点(非递归)
BSTNode *BST_Search(BSTree T,int key)
{
    while(T!=NULL && key!=T->key)   //若树空或等于根节点值,则结束循环
    {
        if(key<T->key)      //小于,则在左子树上查找
            T=T->lchild;    
        else
            T=T->rchild;    //大于,则在右子树上查找
    }
    return T;
}
3. 在二叉排序树中查找值为key的结点(递归)
BSTNode *BST_Search(BSTree T,int key)
{
    if(T==NULL)
        return NULL;    //空树,查找失败
    if(key==T->key)
        return T;
    else if(key<T->key)
        return BST_Search(T->lchild,key);   //左子树中查找
    else   
        return BST_Search(T->rchild,key);   //右子树中查找
}
4. 二叉排序树的插入和删除
//二叉排序树结点
typedef struct BSTNode
{
    int key;
    struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
插入思想:
若原二叉排序树为空,则直接插入结点;否则,若关键字k小于根节点值,则插入到左子树,若关键字k大于根节点值,则插入到右子树。
在二叉排序树插入关键字为k的新结点(递归实现),空间复杂度:O(h)
int BST_Insert(BSTree &T,int k)
{
    if(T==NULL)         //原树为空,新插入的结点为根节点
    {
        T=(BSTree)malloc(sizeof(BSTNode));
        T->key=k;
        T->lchild=T->rchild=NULL;
        return 1;       //返回1,插入成功!
    }
    else if(k==T->key)  //树中存在相同关键字的结点,插入失败
        return 0;
    else if(k<T->key)   //插入到左子树
        return BST_Insert(T->lchild,k);
    else                //插入到右子树
        return BST_Insert(T->rchild,k);
}
按照str[]中的关键字序列建立二叉排序树
void Creat_BST(BSTree &T,int str[],int n)
{
    T=NULL;     //初始时T为空树
    int i=0;
    while(i<n)  //依次将每个关键字插入到二叉排序树中
    {
        BST_Insert(T,str[i]);
        i++;
    }
}
二叉排序树的删除:
先找到目标结点:
1.若被删除节点z是叶节点,则直接删除,不会破坏二叉排序树的性质.
2.若结点z只有一颗左子树或右子树,则让z的子树成为z父节点的子树,替代z的位置.
3.若结点z有左、右两颗子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一或第二种情况.
左子树结点值 < 根结点值 < 右子树结点值
中序遍历可以得到一个递增的有序序列
z的后继:z的右子树中最左下结点(该节点一定没有左子树)
z的前驱:z的左子树中最右下结点(该节点一定没有右子树)

七、平衡二叉树

平衡二叉树(Balanced Binary Tree),检测平衡数(AVL)树----树上任一结点的左子树和右子树的高度之差不超过1.
结点的平衡因子=左子树高-右子树高
平衡二叉树结点的平衡因子的值只可能是-1,0,1
只要有任意节点的平衡因子绝对值大于1,就不是平衡二叉树

//平衡二叉树结点

#include<stdio.h>
typedef struct AVLNode
{
    int key;        //数据域
    int balance;    //平衡因子
    struct AVLNode *lchild,*rchild;
}AVLNode,*AVLTree;
1. LL—在A的左孩子的左子树中插入导致不平衡
LL平衡旋转(右单旋转)。
由于在结点A的左孩子(L)的左子树(L)上插入了新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要一次向右的旋转操作。将A的左孩子B向右上旋转代替A成为根节点,将A结点向右下旋转成为B的右子树的根节点,而B原右子树则作为A结点左子树。
代码思路:
    指针指向:f->A,p->B,gf->lchild/rchild=A;
    实现f向右下旋转,p向右上旋转:
        其中,f是爹,p为左孩子,gf是f他爹
    1.f->lchild=p->rchild;
    2.p->rchild=f;
    3.gf.lchild/rchild=p;
2. RR—在A的右孩子的右子树中插入导致不平衡
RR平衡旋转(左单旋转)。
由于在结点A的右孩子®的右子树®上插入了新结点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要一次向左的旋转操作。将A的右孩子B向左上旋转代替A成为根节点,将A结点向左下旋转成为B的左子树的根节点,而B的原左子树则作为A结点的右子树。
代码思路:
        指针指向:f->A,p->B,gf->lchild/rchild=A;
        实现f向左下旋转,p向左上旋转:
            其中,f是爹,p为左孩子,gf是f他爹
        1.f->rchild=p->lchild;
        2.p->lchild=f;
        3.gf.lchild/rchild=p;
3. LR—在A的左孩子的右子树中插入导致不平衡
LR平衡旋转(先左旋,再右旋)
由于在A的左孩子(L)的右子树(R)上插入新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先左旋后右旋,先将A结点的左孩子B的右子树的根结点C向左上旋转提升到B结点的位置,然后再把该C结点向右上旋转提升到A结点的位置。
4. RL—在A的右孩子的左子树中插入导致不平衡
LR平衡旋转(先右旋,再左旋)
由于在A的右孩子®的左子树(L)上插入新结点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先右旋后左旋。先将A结点的右孩子B的左子树的根结点C向右上旋转提升到B结点的位置,然后再把该C结点向左上旋转提升到A结点的位置。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wings(hha)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值