数据结构与算法学习笔记6:BST二叉搜索树

二叉搜索树 BST

BST的创建

  • 创建思路:

    • 如果树为空,那么来了就是根
    • 非空,新结点和父节点比较,比父节点大进右子树,比父节点小进左子树
  • 创建代码

    void AddNode(BinaryTree **pTree,int nNum){
        //数值放入节点
        BinaryTree *pTemp = NULL;
        pTemp = (BinaryTree*)malloc(sizeof(BinaryTree));
        pTemp->nValue = nNum;
        pTemp->pLeft = NULL;
        pTemp->pRight = NULL;
    
        //放入树中
        if(*pTree == NULL){ //当前树空,直接作为根节点
            *pTree = pTemp;
            return;
        }
        BinaryTree *pNode = *pTree;
        while (pNode) {
            if (pNode->nValue > nNum) {
                if (pNode->pLeft == NULL) {
                    pNode->pLeft = pTemp;
                    return;
                }
                pNode = pNode->pLeft;
            }else if (pNode->nValue < nNum) {
                if (pNode->pRight == NULL) {
                    pNode->pRight = pTemp;
                    return;
                }
                pNode = pNode->pRight;
            }else {
                printf("date error.\n");
                free(pTemp);
                pTemp = NULL;
                return;
            }
        }
    }
    
    BinaryTree *CreateBST(){
        int nNum;
        int nLength;
        int i;
    
        BinaryTree *pTree = NULL;
        printf("请输入节点个数:\n");
        scanf("%d",&nLength);
    
        for (i = 0; i < nLength; i++) {
            scanf("%d",&nNum);
            //节点添加
            AddNode(&pTree,nNum);
        }
        return pTree;
    }
    

BST的删除

  • 删除思路:

    • ① 查找(找要删除的节点及其父亲 or 找到父亲,如果不找到父亲节点,那么删除后就找不回去了)

    • ② 删除(子节点数量分析)

      • 1)0,删除
      • 2)1,当前节点的父节点和子节点进行连接(爷孙连接),删除
      • 3)2,找右子树的最小或者左子树的最大,进行替换删除(先覆盖,再删除)
        • PS:2个孩子的情况是覆盖后再删除,那么这个删除的部分和0,1的删除情况又相同,代码相等,因此可以在写代码的时候先写2个孩子的,将其转变为0,1的情况,降低代码冗余。
  • 代码:

    void Search(BinaryTree *pTree,int nNum,BinaryTree **pDel,BinaryTree **pDelFather){
        while (pTree) {
            if(pTree->nValue == nNum){
                *pDel = pTree;
                return;
            }
            else if(pTree->nValue > nNum){
                //左侧
                *pDelFather = pTree;
                pTree = pTree->pLeft;
            }
            else {
                //右侧
                *pDelFather = pTree;
                pTree = pTree->pRight;
            }
        }
        *pDelFather = NULL;
    }
    
    void DeleteBSTNode(BinaryTree **pTree,int nNum){
        //查找
        BinaryTree *pDel = NULL;
        BinaryTree *pDelFather = NULL;
        //搜索
        Search(*pTree,nNum,&pDel,&pDelFather);
        //删除
        if(pDel == NULL)    return;     //未找到,不用删
        BinaryTree *pMark = NULL;       //标记
            //两个孩子情况的处理
            if(pDel->pLeft != NULL && pDel->pRight != NULL){
                //左的最右or右的最左来进行替换
                pMark = pDel;
                //本代码选择左的最右
                pDelFather = pDel;
                pDel = pDel->pLeft;
                while (pDel->pRight != NULL) {
                    pDelFather = pDel;
                    pDel = pDel->pRight;
                }
                //值覆盖
                pMark->nValue = pDel->nValue;
            }
        //0个or1个孩子的处理
        if(pDelFather == NULL){     //被删除节点是根,换根的情况
            *pTree = pDel->pLeft ? pDel->pLeft : pDel->pRight;
                //左孩子非空则左孩子成为新根,右孩子非空则右孩子成为新根
            free(pDel);
            pDel = NULL;
            return;
        } 
        else{       //被删除节点非根,需要判断爷孙相连是左侧还是右侧相连
            if (pDel == pDelFather->pLeft) {
                pDelFather->pLeft = pDel->pLeft ? pDel->pLeft : pDel->pRight;
            }else {
                pDelFather->pRight = pDel->pLeft ? pDel->pLeft : pDel->pRight;
            }
            free(pDel);
            pDel = NULL;
            return;
        }
    }
    

如何将一棵排序二叉树转变为有序的双向链表?

img

  • 思路:

    • 中序遍历,right指向下一个,left指向上一个
  • 代码:

    void BstToList(BinaryTree *pTree,BinaryTree **pHead,BinaryTree **pTail){
        if (pTree == NULL) return;  //空的话就不需要处理了
        //处理左子树
        BstToList(pTree->pLeft, pHead, pTail);
        //处理节点  加到链表中  
        if (*pHead == NULL) {   //链表为空 直接做表头
            *pHead = pTree;
        }
        else{   //链表不为空  原来尾的下一个(Right)就是当前节点,当前节点的前一个就是原来的尾
            (*pTail)->pRight = pTree;
            pTree->pLeft = *pTail;
        }
        *pTail = pTree; //新来节点成为新的尾
        //处理右子树
        BstToList(pTree->pRight, pHead, pTail);
    }
    
    void Print(BinaryTree *pHead){
        while (pHead) {
            printf("%d  ",pHead->nValue);
            pHead = pHead->pRight;
        }
    }
    

BST的旋转

右旋(左的左引起的不平衡时进行)
image-20220505140505467 image-20220505154734212
父子关系:
  • A的左孩子 = E ;B的右孩子 = A ; X的孩子 = B

  • E的父亲 = A;A的父亲 = B;B的父亲 = X

    //右旋代码
    void RightRotate(BinaryTree *pNode){
        if (pNode == NULL || pNode->pLeft == NULL) return;
        BinaryTree *pMark = pNode->pLeft;
        //三个孩子的关系
        pNode->pLeft = pMark->pRight;
        pMark->pRight = pNode;
        if (pNode->pFather == NULL) {
            pTree = pMark;
        }else{
            if (pNode == pNode->pFather->pLeft) {
                pNode->pFather->pLeft = pMark;
            }
            else{
                pNode->pFather->pRight = pMark;
            }
        }
        //三个父亲的关系
        if (pNode->pLeft != NULL) {
            pNode->pLeft->pFather = pNode;
        }
        pMark->pFather = pNode->pFather;
        pNode->pFather = pMark;
    }
    
  • 整体代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Node{
        int nValue;
        struct Node *pLeft;
        struct Node *pRight;
        struct Node *pFather;	//右旋时需要用到父子关系,所以创建树时注意需要该信息
    }BinaryTree;
    
    BinaryTree *pTree = NULL;
    
    BinaryTree* CreateBinaryTree(){
        //根
        BinaryTree* Root = NULL;
        Root = (BinaryTree*)malloc(sizeof(BinaryTree));
        Root->nValue = 1;
        Root->pFather = NULL;
        //根的左
        Root->pLeft = (BinaryTree*)malloc(sizeof(BinaryTree));
        Root->pLeft->nValue = 2;
        Root->pLeft->pFather = Root;
        //根的右
        Root->pRight = (BinaryTree*)malloc(sizeof(BinaryTree));
        Root->pRight->nValue = 3;
        Root->pRight->pFather = Root;
        //根的左的左
        Root->pLeft->pLeft = (BinaryTree*)malloc(sizeof(BinaryTree));
        Root->pLeft->pLeft->nValue = 4;
        Root->pLeft->pLeft->pLeft = NULL;
        Root->pLeft->pLeft->pRight = NULL;
        Root->pLeft->pLeft->pFather = Root->pLeft;
        //根的左的右
        Root->pLeft->pRight = (BinaryTree*)malloc(sizeof(BinaryTree));
        Root->pLeft->pRight->nValue = 5;
        Root->pLeft->pRight->pLeft = NULL;
        Root->pLeft->pRight->pRight = NULL;
        Root->pLeft->pRight->pFather = Root->pLeft;
    
        return Root;
    }
    
    void RightRotate(BinaryTree *pNode){
        if (pNode == NULL || pNode->pLeft == NULL) return;
        BinaryTree *pMark = pNode->pLeft;
        //三个孩子的关系
        pNode->pLeft = pMark->pRight;
        pMark->pRight = pNode;
        if (pNode->pFather == NULL) {
            pTree = pMark;
        }else{
            if (pNode == pNode->pFather->pLeft) {
                pNode->pFather->pLeft = pMark;
            }
            else{
                pNode->pFather->pRight = pMark;
            }
        }
        //三个父亲的关系
        if (pNode->pLeft != NULL) {
            pNode->pLeft->pFather = pNode;
        }
        pMark->pFather = pNode->pFather;
        pNode->pFather = pMark;
    }
    
    void PreorderTraversal(BinaryTree *pTree){
        if (pTree == NULL)  return;
        //打印
        printf("%d  ",pTree->nValue);
        //左子树
        PreorderTraversal(pTree->pLeft);
        //右子树
        PreorderTraversal(pTree->pRight);
    }//前序遍历
    
    int main()
    {
        pTree = CreateBinaryTree();
        PreorderTraversal(pTree);
        printf("\n");
        RightRotate(pTree);
        PreorderTraversal(pTree);
        printf("\n");
    
        return 0;
    }
    
左旋(右的右引起的不平衡时进行)
image-20220505161212312
父子关系:
  • C的左孩子 = A ;C的右孩子 = E ; X的孩子 = C

  • A的父亲 = C;E的父亲 = C;C的父亲 = X

//左旋代码
void LeftRotate(BinaryTree *pNode){
    if (pNode == NULL || pNode->pRight == NULL) return;
    BinaryTree *pMark = pNode->pRight;
    //三个孩子的关系
    pNode->pRight = pMark->pLeft;
    pMark->pLeft = pNode;
    if (pNode->pFather == NULL) {
        pTree = pMark;
    }else{
        if (pNode == pNode->pFather->pLeft) {
            pNode->pFather->pLeft = pMark;
        }
        else{
            pNode->pFather->pRight = pMark;
        }
    }
    //三个父亲的关系
    if (pNode->pRight != NULL) {
        pNode->pRight->pFather = pNode;
    }
    pMark->pFather = pNode->pFather;
    pNode->pFather = pMark;
}
//其实和右旋代码一样,把left和right互相替换掉就可以了
左右旋VS右左旋
  • 左的右引起的不平衡 -> 左右旋

    • 举例一下~

      image-20220505163305522

  • 右的左引起的不平衡 -> 右左旋

  • 直接调用右旋和左旋的函数即可,无需重写左右旋 or 右左旋的函数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

97Marcus

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

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

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

打赏作者

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

抵扣说明:

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

余额充值