AVL树的插入

理论部分是看的《数据结构(C语言版)》Ellis等著,补全了相应的代码并且添加了一些自己的注释以及对一些理论部分的分析。

#include<iostream>
using namespace std;

struct TreeNode {
	int val;
	int bf;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 };

class Solution {
public:

	void avl_insert(TreeNode **parent, int x, int *unbalanced);
	void left_rotation(TreeNode **parent, int *unbalanced);
	void right_rotation(TreeNode **parent, int *unbalanced);
};

inline void Solution::avl_insert(TreeNode ** parent, int x, int * unbalanced) {
	if (!*parent) {
		// 树为空或者parent节点已经是叶结点
		*unbalanced = true;
		*parent = new TreeNode(x);
		(*parent)->left = (*parent)->right = NULL;
		(*parent)->bf = 0;
	}
	else if (x < (*parent)->val) {
		// 插入到左子树
		avl_insert(&(*parent)->left, x, unbalanced);
		if (*unbalanced) {
			switch ((*parent)->bf)
			{
			case -1:
				(*parent)->bf = 0;
				*unbalanced = false;
				break;
			case 0:
				(*parent)->bf = 1;
				break;
			case 1:
				left_rotation(parent, unbalanced);
			}
		}
	}
	else if (x > (*parent)->val) {
		avl_insert(&(*parent)->right, x, unbalanced);
		if (*unbalanced) {
			switch ((*parent)->bf)
			{
			case -1:
				right_rotation(parent, unbalanced);
				break;
			case 0:
				(*parent)->bf = -1;
				break;
			case 1:
				(*parent)->bf = 0;
				*unbalanced = false;
				break;
			}
		}
	}
	else {
		*unbalanced = false;
		cout << "key is already exists." << endl;
	}
}

// bf=1的左子树上插入才会进到这个函数
void Solution::left_rotation(TreeNode ** parent, int * unbalanced)
{
	TreeNode *grand;
	TreeNode *child;
	child = (*parent)->left;
	if (child->bf == 1) {
		// LL
		(*parent)->left = child->right;
		child->right = *parent;
		(*parent)->bf = 0;
		(*parent) = child;
	}
	else {
		//LR
		grand = child->right;
		child->right = grand->left;
		grand->left = child;
		(*parent)->left = grand->right;
		grand->right = *parent;
		switch (grand->bf)
		{
		case 1:
			//当前的parent是2,且新节点是被插入到grand的左子树(此时grand的bf已经被更新),
			// 旋转以后,parent的左子树是grand的右子树,且在旋转以前,parent最高的左子树是grand的左子树,
			// 所以现在parent的左子树被减少了两层(child和grand),而且新的左子树(grand的右子树)还比grand的左子树少一层,
			// 所有现在parent的左子树相当于比以前少三层,所以2-3=-1.
			// child的bf同理.
			child->bf = 0;
			(*parent)->bf = -1;
			break;
		case 0:
			(*parent)->bf = child->bf = 0;
			break;
		case -1:
			(*parent)->bf = 0;
			child->bf = 1;
		}
		*parent = grand;
	}
	(*parent)->bf = 0;
	*unbalanced = false;
}

void Solution::right_rotation(TreeNode ** parent, int * unbalanced)
{
	TreeNode *grand;
	TreeNode *child;
	child = (*parent)->right;
	if (child->bf == -1) {
		// RR
		(*parent)->right = child->left;
		child->left = *parent;
		(*parent)->bf = 0;
		(*parent) = child;
	}
	else {
		// RL
		grand = child->left;
		child->left = grand->right;
		(*parent)->right = grand->left;
		grand->right = child;
		grand->left = *parent;
		switch (grand->bf)
		{
		case 1:
			(*parent)->bf = 0;
			child->bf = -1;
			break;
		case 0:
			(*parent)->bf = child->bf = 0;
			break;
		case -1:
			(*parent)->bf = 1;
			child->bf = 0;
			break;
		default:
			break;
		}
		(*parent) = grand;
	}
	(*parent)->bf = 0;
	*unbalanced = false;
}

void preOrder(TreeNode* root) {
	if (root) {
		printf("%d ", root->val);
		preOrder(root->left);
		preOrder(root->right);
	}
}
void inOrder(TreeNode* root) {
	if (root) {
		inOrder(root->left);
		printf("%d ", root->val);
		inOrder(root->right);
	}
}

int main() {
	TreeNode *root = NULL;
	int unbalanced = false;
	//int arr[11] = { 15,6,18,3,7,17,20,2,4,13,9 };
	int arr[11] = { 42,17,64,9,84,7,60,8,90,59 };
	Solution solution;
	for (int i = 0; i < 10; i++) {
		//   cout<<arr_x[i].key<<endl;
		solution.avl_insert(&root, arr[i], &unbalanced);
	}
	cout << "前序:" << endl;
	preOrder(root);
	printf("\n");
	cout << "中序" << endl;
	inOrder(root);
	return 0;
}

自己在main里面定义一个数组,纸上画出它的AVL树,再对照前序+后序的输出查看是否正确就可以了.

1. 关于void right_rotation(TreeNode **parent, int *unbalanced);为什么传入的是**parent而不是*parent,因为在函数里面需要修改parent的指向,比如right_rotation(&(*parent)->right,unblanced)的时候,如果在函数里面修改了parent的值,那么函数递归结束返回到当前层的时候,parent->right已经指向了一个新的节点;如果只是单纯的传入*parent,那么在函数中修改parent的指向,当前层是感受不到的。举个例子:

#include<iostream>
using namespace std;

void fun(int *p) {
	cout<<"调用函数中p的地址是:No1-"<<p<<endl;
	p++;
	cout<<"调用函数中p的地址是:No2-"<<p<<endl;
}

int main(){
	int A[2] = {1,2};
	int *p = A;
	cout<<"调用函数前p的地址是:"<<p<<endl;
	fun(p); 
	cout<<"调用函数后p的地址是:"<<p<<endl;
} 

结果如图所示:main里面p的指向并未发生改变。

再看如下函数:

#include<iostream>
using namespace std;

void fun(int **p) {
	cout<<"调用函数中p的地址是:No1-"<<*p<<endl;
	(*p)++;
	cout<<"调用函数中p的地址是:No2-"<<*p<<endl;
}

int main(){
	int A[2] = {1,2};
	int *p = A;
	cout<<"调用函数前p的地址是:"<<*p<<endl;
	fun(&p); 
	cout<<"调用函数后p的地址是:"<<*p<<endl;
} 

p的指向发生了改变。

 

2.

 

三种情况的分析:

          当前的parent是2,且新节点是被插入到grand的左子树(此时grand的bf已经被更新),
          旋转以后,parent的左子树是grand的右子树,且在旋转以前,parent最高的左子树是grand的左子树,
          所以现在parent的左子树被减少了两层(child和grand),而且新的左子树(grand的右子树)还比grand的左子树少一层,
          所有现在parent的左子树相当于比以前少三层,所以2-3=-1.
          child的bf同理.

如果不想理论化的分析那举个例子也可以,因为bf三种情况的分析是普适性的,个例依旧可以得出旋转后各个节点的bf的值。

LR模式:parent=B,child=E,grand=G.且grand的bf是-1           

            (*parent)->bf = 1;
            child->bf = 0;
            break;

旋转后:child也就是E的bf是0,parent的bf是1,符合。

推而广之,普适情况和特例中child,parent,grand的bf值都是想符合的。不想使用逻辑推导就用特例把旋转后的节点的bf求出来。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值