LeetCode108 —— Convert Sorted Array to Binary Search Tree

这道题的意思是给你一个排好序的数组,如何建立一棵平衡二叉查找树。一开始还是不知道怎么去搞的,但是想了想这是道简单题,那应该不会很复杂,因为数组是排好序的,所以肯定有什么小trick。

于是我就直接打开了讨论区去看(真的想不到什么简单的方法去做),就发现做法还是蛮简洁的,既然是排序后的数组,且左子树和右子树相差不超过1,那么root肯定就是数组的中位数,然后再递归一下就好了。代码如下:

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        if(nums.size() == 0) return NULL;
        else if(nums.size() == 1) return new TreeNode(nums[0]);
        int middle = nums.size() / 2;
        TreeNode* root = new TreeNode(nums[middle]);

        vector<int> leftNums(nums.begin(), nums.begin() + middle);
        vector<int> rightNums(nums.begin() + middle + 1, nums.end());

        root->left = sortedArrayToBST(leftNums);
        root->right = sortedArrayToBST(rightNums);

        return root;
    }
};

然后的话再扩展一下,如果不是排好序的数组呢?那么这道题就是一个经典的平衡二叉搜索树建树的问题了。就加入输入的序列不是排好序的,而是乱序的,在这种情况下的话就应该用平衡树去解决了。我这里的前两天写的平衡树是链表+递归版本的,当然也可以是数组+循环版本的,后者应该会更高效。

首先是平衡树的定义。

先说一下节点的高度,给定一棵二叉树,节点的高度就是这个节点去到叶子节点的最长路径的长度,也就是说一个节点可能会有很多条路径前往叶子结点,那么去最长的那条路径,路径的上边的条数就是该节点的高度,这里规定叶节点的高度为0。

然后再说一下二叉搜索树,二叉搜索树就是一棵树,假设给定A节点,那么A节点左子树中的所有节点的值都小于A节点,右子树所有节点的值都大于A节点。但是这个就可能出现搜索树严重退化的问题,因此引入了平衡树。

那么平衡树是什么呢?平衡树是一种平衡的二叉搜索树,其中平衡指的是任意节点的左右孩子节点的高度差不超过2。下面详细介绍一下平衡树的建树过程。

一棵树的建立其实就是节点不断插入的过程,这个过程能保证这棵树是一棵搜索树,但是不能保证平衡。因此引入了旋转的概念,旋转共有四种,下面将一一介绍:
1⃣️ LL旋转在这里插入图片描述
可以发现在上图中,D节点插入以后整棵树就不平衡了,因为K2节点的左右孩子节点的高度差为2了。确定不平衡之后还要确定是那种不平衡,当前不平衡的节点为K2,那么就要看新插入的节点的值和节点K2的值的大小了,这里D的值小于K2,LL旋转中的第一个L也就是Left就是这么来的;那么再看D和K2的左孩子节点K1,D的值同样小于K1的值,所以插在K1的左边,这也就是LL旋转中的第二个L也就是Left。因此通过这种方法就可以确定旋转的方式。

这里是LL旋转,那么就要把K1的右子树变成K2的左子树,然后K1自己变成根节点,这样就完成了LL旋转,代码实现如下:

AVLTreeNode* leftLeftRotation(AVLTreeNode* k2)
{
	AVLTreeNode* k1 = k2->left;

	k2->left = k1->right;
	k1->right = k2;

	k2->height = max(height(k2->left), height(k2->right)) + 1;
	k1->height = max(height(k1->left), height(k1->right)) + 1;

	return k1;
} 

2⃣️ RR旋转

如图所示为RR旋转,原理和LL相似,代码实现如下:

{
	AVLTreeNode* k2 = k1->right;

	k1->right = k2->left;
	k2->left = k1;

	k1->height = max(height(k1->left), height(k1->right)) + 1;
	k2->height = max(height(k2->left), height(k2->right)) + 1;

	return k2;
}

3⃣️ LR旋转

如图所示,这种情况下没法通过单一的旋转解决,要两次旋转才能让树恢复平衡,如果是LR旋转的话需要先对K1节点进行RR旋转,然后再对K3进行LL旋转。代码如下:

AVLTreeNode* leftRightRotation(AVLTreeNode* k3)
{
	k3->left = rightRightRotation(k3->left);
	return leftLeftRotation(k3);
}

4⃣️ RL旋转

RL旋转原理同样和LR相似,这里也不加赘述,代码耶很简单:

AVLTreeNode* rightLeftRotation(AVLTreeNode* k3)
{
	k3->right = leftLeftRotation(k3->right);
	return rightRightRotation(k3);
}

然后再附上插入的代码,这里最开始还是有一些容易写错的地方的,但是熟悉了就好,就直接附上递归插入的代码吧:

AVLTreeNode* insert(AVLTreeNode* &root, int key)
{
	if(root == NULL){
		root = new AVLTreeNode(key, NULL, NULL);
	}
	else if(key < root->key){
		/*插入到左子树的情况*/
		root->left = insert(root->left, key);
		if(height(root->left) - height(root->right) == 2){
			if(key < root->left->key)
				root = leftLeftRotation(root);
			else
				root = leftRightRotation(root);
		}
	}
	else if(key >= root->key){
		root->right = insert(root->right, key);
		if(height(root->right) - height(root->left) == 2){
			if(key > root->right->key)
				root = rightRightRotation(root);
			else
				root = rightLeftRotation(root);
		}
	}

	root->height = max(height(root->left), height(root->right)) + 1;
	return root;
}

这里主要就是要注意更新节点的高度以及节点为NULL的时候应该怎么样去处理,也是看对递归的熟悉程度吧。

完整代码如下(含测试用例):

#include<iostream>
#include<algorithm>
using namespace std;

static int arr[]= {3,2,1,4,6,7,16,15,14,13,12,11,10,8,9};
#define TBL_SIZE(a) ( (sizeof(a)) / (sizeof(a[0])) )

struct AVLTreeNode{
public:
	int key;
	int height;
	AVLTreeNode* left;
	AVLTreeNode* right;
	AVLTreeNode(int key, AVLTreeNode* left, AVLTreeNode* right):
		key(key), height(0), left(left), right(right){}
};


int max(int a, int b){
	return a > b ? a : b;
}

int height(AVLTreeNode* nowNode){

	if(nowNode != NULL){
		return nowNode->height;
	}
	return 0;
}

AVLTreeNode* leftLeftRotation(AVLTreeNode* k2)
{
	AVLTreeNode* k1 = k2->left;

	k2->left = k1->right;
	k1->right = k2;

	k2->height = max(height(k2->left), height(k2->right)) + 1;
	k1->height = max(height(k1->left), height(k1->right)) + 1;

	return k1;
} 

AVLTreeNode* rightRightRotation(AVLTreeNode* k1)
{
	AVLTreeNode* k2 = k1->right;

	k1->right = k2->left;
	k2->left = k1;

	k1->height = max(height(k1->left), height(k1->right)) + 1;
	k2->height = max(height(k2->left), height(k2->right)) + 1;

	return k2;
}

AVLTreeNode* leftRightRotation(AVLTreeNode* k3)
{
	k3->left = rightRightRotation(k3->left);
	return leftLeftRotation(k3);
}

AVLTreeNode* rightLeftRotation(AVLTreeNode* k3)
{
	k3->right = leftLeftRotation(k3->right);
	return rightRightRotation(k3);
}

AVLTreeNode* insert(AVLTreeNode* &root, int key)
{
	if(root == NULL){
		root = new AVLTreeNode(key, NULL, NULL);
	}
	else if(key < root->key){
		/*插入到左子树的情况*/
		root->left = insert(root->left, key);
		if(height(root->left) - height(root->right) == 2){
			if(key < root->left->key)
				root = leftLeftRotation(root);
			else
				root = leftRightRotation(root);
		}
	}
	else if(key >= root->key){
		root->right = insert(root->right, key);
		if(height(root->right) - height(root->left) == 2){
			if(key > root->right->key)
				root = rightRightRotation(root);
			else
				root = rightLeftRotation(root);
		}
	}

	root->height = max(height(root->left), height(root->right)) + 1;
	return root;
}

void preOrder(AVLTreeNode* root)
{
	if(root != NULL){
		cout << root->key << ' ';
		preOrder(root->left);
		preOrder(root->right);
	}
}


int main()
{
	int i,ilen;
	AVLTreeNode* Root=NULL;

	cout << "== 依次添加: ";
	int size = 15; 
	for(i = 0; i < 15; i++)
	{
		cout << arr[i] << " ";
		insert(Root, arr[i]);
	}
	cout << endl;
	cout << "preOrder: ";
	preOrder(Root);
	return 0;
}

本博客图片源自这里

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值