前言
上一篇博客讲完了二叉搜索树,二叉搜索树在分配单元上有一些挺好的做法,非极端情况下查找的时间复杂度为 O ( l o g n ) O(logn) O(logn),但是请看下面这种情况,如果我们要查找数字6,时间复杂度会退化为 O ( n ) O(n) O(n),跟普通链表没什么差别了。所以我们需要一种方式来平衡一下这棵树,使得我们可以一直保持着比较快的搜索时间。
重要概念
那么我们应该在什么时候平衡呢?在每一次插入新节点时进行平衡,平衡的操作包括左单旋,右单旋,左双旋,右双旋。听起来十分复杂,但其实并没有,我们可以通过画图使得这个步骤变简单。
思考逻辑
我初学的时候直接理解的代码,非常难受,后来老师上课的时候手绘了一次旋转的全过程,于是我印象深刻。我理解了以下两个图的变换从而写的代码。
单旋转
把 k 1 k_1 k1降级为 k 2 k_2 k2的孩子,然后把 k 2 k_2 k2的Y树分配给 k 1 k_1 k1实现平衡,这里要注意XYZ代表子树,这些子树可以是空的,只要满足了 k 1 k_1 k1的左右子树的高度相差为2,不影响这个转换的过程。
BSTree* RtSgRotation(BSTree* root) //右单旋(right single rotation)
{
BSTree* temp;
temp = root->lchild;
root->lchild = temp->rchild;
temp->rchild = root;
root->height = getMax(getHeight(root->lchild), getHeight(root->rchild))+1;
temp->height = getMax(getHeight(temp->lchild), root->height)+1;
return temp;
}
BSTree* LtSgRotation(BSTree* root) //左单旋(left single rotation)
{
BSTree* temp;
temp = root->rchild;
root->rchild = temp->lchild;
temp->lchild = root;
root->height = getMax(getHeight(root->lchild), getHeight(root->rchild))+1;
temp->height = getMax(getHeight(temp->rchild), root->height)+1;
return temp;
}
双旋转
双旋转可以理解为两次单旋转的结合,把 x x x拆开看是精髓所在,其他同上的单旋转!图如下:
BSTree* RtDbRotation(BSTree* root) //右双旋(right double rotation)= 左单旋+右单旋
{
root->lchild = LtSgRotation(root->lchild);
return RtSgRotation(root);
}
BSTree* LtDbRotation(BSTree* root) //左双旋(left double rotation)= 右单旋+左单旋
{
root->rchild = RtSgRotation(root->rchild);
return LtSgRotation(root);
}
全部代码
#include <stdio.h>
#include <stdlib.h>
typedef struct BST
{
int data;
int height;
struct BST* lchild;
struct BST* rchild;
}BSTree;
BSTree* CreateBSTNode(int x)
{
BSTree* newNode = (BSTree*)malloc(sizeof(BSTree));
newNode->data = x;
newNode->height = 0;
newNode->lchild = NULL;
newNode->rchild = NULL;
return newNode;
}
int getHeight(BSTree* root) //求一个节点的高
{
if(!root)
{
return -1;
}
else
{
return root->height;
}
}
int getMax(int a, int b)
{
if(a > b)
return a;
return b;
}
BSTree* RtSgRotation(BSTree* root) //右单旋(right single rotation)
{
BSTree* temp;
temp = root->lchild;
root->lchild = temp->rchild;
temp->rchild = root;
root->height = getMax(getHeight(root->lchild), getHeight(root->rchild))+1;
temp->height = getMax(getHeight(temp->lchild), root->height)+1;
return temp;
}
BSTree* LtSgRotation(BSTree* root) //左单旋(left single rotation)
{
BSTree* temp;
temp = root->rchild;
root->rchild = temp->lchild;
temp->lchild = root;
root->height = getMax(getHeight(root->lchild), getHeight(root->rchild))+1;
temp->height = getMax(getHeight(temp->rchild), root->height)+1;
return temp;
}
BSTree* RtDbRotation(BSTree* root) //右双旋(right double rotation)= 左单旋+右单旋
{
root->lchild = LtSgRotation(root->lchild);
return RtSgRotation(root);
}
BSTree* LtDbRotation(BSTree* root) //左双旋(left double rotation)= 右单旋+左单旋
{
root->rchild = RtSgRotation(root->rchild);
return LtSgRotation(root);
}
BSTree* CreateBSTree(BSTree* root, int x)
{
if(!root)
{
root = CreateBSTNode(x);
return root;
}
else if(x < root->data) // 插入的时候,x会被插入在左子树
{
root->lchild = CreateBSTree(root->lchild, x);
if(getHeight(root->lchild) - getHeight(root->rchild) == 2) //如果左右孩子的高相差2,就需要右旋转来解决了
{
if(x < root->lchild->data)
root = RtSgRotation(root); // 如果x比左孩子的数据小,说明被插入到了左孩子的左孩子,那么就是右单旋
else
root = RtDbRotation(root); // 如果x比左孩子的数据大,说明被插入到了左孩子的右孩子,那么就是右双旋
}
}
else if(x > root->data) // 插入的时候,x会被插入在右子树
{
root->rchild = CreateBSTree(root->rchild, x);
if(getHeight(root->rchild) - getHeight(root->lchild) == 2) //如果右左孩子的高相差2,就需要左旋转来解决了
{
if(x > root->rchild->data)
root = LtSgRotation(root); // 如果x比右孩子的数据大,说明被插入到了右孩子的右孩子,那么就是左单旋
else
root = LtDbRotation(root); // 如果x比右孩子的数据小,说明被插入到了右孩子的左孩子,那么就是左双旋
}
}
root->height = getMax(getHeight(root->lchild), getHeight(root->rchild))+1;
return root;
}
void preOrder(BSTree* root)
{
if(root)
{
printf("%d ", root->data);
preOrder(root->lchild);
preOrder(root->rchild);
}
}
void midOrder(BSTree* root)
{
if(root)
{
midOrder(root->lchild);
printf("%d ", root->data);
midOrder(root->rchild);
}
}
void postOrder(BSTree* root)
{
if(root)
{
postOrder(root->lchild);
postOrder(root->rchild);
printf("%d ", root->data);
}
}
BSTree* find(BSTree* root, int x)
{
if(!root)
return NULL;
if(x < root->data)
return find(root->lchild, x);
else if(x > root->data)
return find(root->rchild, x);
else
return root;
}
/*
BSTree* find(BSTree* root, int x)
{
BSTree* curNode = root;
//BSTree* curParNode;
int flag = 0;
while(curNode->data != x && !curNode)
{
//curParNode = curNode;
if(x < curNode->data)
curNode = curNode->lchild;
else if(x > curNode->data)
curNode = curNode->rchild;
else
flag = 1;
}
if(flag == 1)
return curNode;
else
return NULL;
}
*/
BSTree* findleftmax(BSTree* root)
{
if(root->rchild)
return findleftmax(root->rchild);
return root;
}
BSTree* DeleteBSTNode(BSTree* root, int x)
{
/*三种情况:
**1. 没有子结点
**2.一个子结点
**3.两个子结点
**特殊考虑删的是根结点
*/
BSTree* temp;
if(!root)
return NULL;
else if(x < root->data)
root->lchild = DeleteBSTNode(root->lchild, x);
else if(x > root->data)
root->rchild = DeleteBSTNode(root->rchild, x);
else
{
if(root->lchild && root->rchild)
{
temp = findleftmax(root->lchild);
root->data = temp->data;
root->lchild = DeleteBSTNode(root->lchild, root->data);
}
else
{
temp = root;
if(root->lchild)
{
root = root->lchild;
}
else if(root->rchild)
{
root = root->rchild;
}
else
{
root = NULL;
}
free(temp);
temp = NULL;
}
}
return root;
}
int main()
{
BSTree* root = NULL; //NULL should always be initialized to the root!!!
int x;
int flag = 1;
while(flag)
{
printf("Enter the data of BST node: ");
scanf("%d", &x);
getchar();
root = CreateBSTree(root, x);
printf("Enter 0 to stop: ");
scanf("%d", &flag);
getchar();
}
preOrder(root);
printf("\n");
printf("Please enter the value to delete: ");
scanf("%d", &x);
DeleteBSTNode(root, x);
preOrder(root);
return 0;
}