这道题的意思是给你一个排好序的数组,如何建立一棵平衡二叉查找树。一开始还是不知道怎么去搞的,但是想了想这是道简单题,那应该不会很复杂,因为数组是排好序的,所以肯定有什么小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;
}
本博客图片源自这里