力扣108. 将有序数组转换为二叉搜索树(中序遍历,递归)
https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5

中序遍历,递归
思路:
众所周知,二叉搜索树的中序遍历是一个升序序列。
将有序数组作为输入,可以把该问题看做 根据中序遍历序列创建二叉搜索树。
下面是一些关于 BST 的知识。
只有中序遍历不能唯一确定一棵二叉搜索树。
先序+后序遍历不能唯一确定一棵二叉搜索树。
先序/后序遍历和中序遍历的关系:inorder = sorted(postorder) = sorted(preorder),
中序+后序、中序+先序可以唯一确定一棵二叉树。
注意:一个附件条件:树的高度应该是平衡的、例如:每个节点的两棵子树高度差不超过 1。
高度平衡意味着每次必须选择中间数字作为根节点。这对于奇数个数的数组是有用的,但对偶数个数的数组没有预定义的选择方案。
对于偶数个数的数组,要么选择中间位置左边的元素作为根节点,要么选择中间位置右边的元素作为根节点,不同的选择方案会创建不同的平衡二叉搜索树。
方法一:中序遍历:始终选择中间位置左边元素作为根节点;方法一始终选择中间位置左边的元素作为根节点。
int mid = (j + i) / 2
方法二:中序遍历:始终选择中间位置右边元素作为根节点;方法二始终选择中间位置右边的元素作为根节点。(i+j)奇数加一
int mid = (j + i) % 2 == 1 ? (j + i) / 2 + 1 : (j + i) / 2;
方法三:中序遍历:选择任意一个中间位置元素作为根节点;方法一和二会生成不同的二叉搜索树,这两种答案都是正确的。
如果 (i+j)奇数,随机选择加一或者加零
int mid = (j + i) % 2 == 1 ? (j + i) / 2 + randint(0, 1) : (j + i) / 2;
复杂度分析
时间复杂度:O(N),每个元素只访问一次。
空间复杂度:O(N),二叉搜索树空间 O(N),递归栈深度 O(logN)。
提供方法一解法:
#include "stdafx.h"
#include<queue>
#include<vector>
#include <iostream>
using namespace std;
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution
{
public:
TreeNode* sortedArrayToBST(vector<int>& nums)
{
//鲁棒性
if (nums.size() == 0)return nullptr;
//根节点的数值下标
int mid = (nums.size() - 1 + 0) / 2;
//创建根节点
TreeNode* head = new TreeNode(nums[mid]);
//左子树,递归
if (0 <= mid - 1)//防止越界
head->left = recursive(nums, 0, mid - 1);
//右子树,递归
if (mid + 1 <= nums.size() - 1)
head->right = recursive(nums, mid + 1, nums.size() - 1);
return head;
}
TreeNode* recursive(vector<int>& nums, int i, int j)
{
int mid = (j + i) / 2;
TreeNode* temp = new TreeNode(nums[mid]);
if (i <= mid - 1)
temp->left = recursive(nums, i, mid - 1);
if (mid + 1 <= j)
temp->right = recursive(nums, mid + 1, j);
return temp;
}
};
int main()
{
vector<int>nums;
nums.push_back(-10); nums.push_back(-3); nums.push_back(0); nums.push_back(5); nums.push_back(9);
Solution s;
auto result = s.sortedArrayToBST(nums);
return 0;
}
附:二叉搜索树BST:Binary Search Trees
二叉排序树(二叉查找树、二叉搜索树)
1、二叉搜索树的特点
什么是二叉查找树:二叉搜索树的特点:对于树中的每个节点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中所有关键字值大于X的关键字值。
根据这个性质,对一个二叉树进行中序遍历,如果是单调递增的,则可以说明这个树是二叉搜索树。
2、二叉搜索树的查找
过程:首先和根节点进行比较,如果等于根节点,则返回。如果小于根节点,则在根节点的左子树进行查找。如果大于根节点,则在根节点的右子树进行查找。
/* 查找以t为根节点的树中,是否包含x */
Position Find(ElementType x, SearchTree t)
{
if (t == NULL) {
return NULL;
} else if (x < t->element) {
return Find(x, t->left);
} else if (x > t->element) {
return Find(x, t->right);
} else {
return t;
}
}
3、查找最大值和最小值
查找最大值:从根开始,如果有右儿子,则向右进行。直到右儿子为空,则当前节点为最大值。
迭代实现
Position FindMax(SearchTree t)
{
if (t == NULL) {
return NULL;
}
while (t->right != NULL)
{
t = t->right;
}
return t;
}
查找最小值:从根开始,如果有左儿子,则向左进行。直到左儿子为空,则当前节点为最小值。
递归实现
Position FindMin(SearchTree t)
{
if (t == NULL) {
return NULL;
} else if (t->left == NULL) {
return t;
} else {
return FindMin(t->left);
}
}
4、二叉搜索树的插入
二叉搜索树的插入过程和查找类似。新插入的节点一般在遍历的路径上的最后一点上,即叶子节点。如果待插入的数据比当前节点的数据大,并且当前节点的右儿子为空,则将待插入的节点插到右儿子位置上。如果右儿子不为空,则再递归的遍历右儿子。如果小于当前节点,则对左儿子做类似的处理就行。
SearchTree Insert(ElementType x, SearchTree t)
{
if (t == NULL) {
/* 插入第一个节点 */
t = (SearchTree)malloc(sizeof(struct TreeNode));
if (t == NULL) {
return NULL;
}
t->element = x;
t->left = NULL;
t->right = NULL;
} else if (x < t->element) {
t->left = Insert(x, t->left);
} else if (x > t->element) {
t->right = Insert(x, t->right);
}
return t;
}
5、二叉搜索树的删除
二叉搜索树的删除分为以下几个情况:
1、待删除的节点是一个叶子节点,即它没有左右儿子。此时只要将它的父节点指向NULL即可。
2、如果节点有一个儿子,则该节点可以在其父节点调整指针绕过该节点后删除。
3、如果有两个儿子,一般的删除策略是用其右子树中最小的数据代替该节点的数据并递归地删除那个节点。因为右子树中最小地节点不可能有左儿子(如果有,则说明不是最小的),所以第二次删除更容易。(其实也就是将有两个儿子的情况转为容易处理的情况1或者2)。
/* 删除策略
* 1、如果待删除节点只有一个儿子,则将该节点的父节点的儿子节点指针指向该节点的儿子节点,
* 然后删除该节点.
* 2、如果待删除节点有两个儿子,用右子树中最小的数据代替该节点的数据并递归地删除那个节点。
* 因为右子树中最小地节点不可能有左儿子,所以第二次删除更容易.
*/
SearchTree Delete(ElementType x, SearchTree t)
{
Position tmp;
if (t == NULL) {
return NULL;
}
if (x < t->element) {
t->left = Delete(x, t->left);
} else if (x > t->element) {
t->right = Delete(x, t->right);
} else if (t->left && t->right) {
tmp = FindMin(t->right);
t->element = tmp->element;
t->right = Delete(t->element, t->right);
} else {
tmp = t;
if (t->left) {
t = t->left;
} else if (t->right) {
t = t->right;
}
free(tmp);
tmp = NULL;
}
return t;
}
6、建立一棵空树
SearchTree MakeEmpty(SearchTree t)
{
if (t != NULL) {
MakeEmpty(t->left);
MakeEmpty(t->right);
free(t);
t = NULL;
}
return NULL;
}