数据结构与算法学习笔记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; } }
如何将一棵排序二叉树转变为有序的双向链表?
-
思路:
- 中序遍历,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](https://i-blog.csdnimg.cn/blog_migrate/ca8477f45b9b2e4044f981261cdd92ec.png)
![image-20220505154734212](https://i-blog.csdnimg.cn/blog_migrate/9f46485f8f4f84517976d2183d040c2e.png)
父子关系:
-
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](https://i-blog.csdnimg.cn/blog_migrate/f0e2df9c76850355e8dc18657f556de5.png)
父子关系:
-
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右左旋
-
左的右引起的不平衡 -> 左右旋
-
举例一下~
-
-
右的左引起的不平衡 -> 右左旋
-
直接调用右旋和左旋的函数即可,无需重写左右旋 or 右左旋的函数。