1,二叉搜索树的插入
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode *t = new TreeNode(val);
if (root == NULL) return t;
// 作为一个叶子节点插入树中
TreeNode *p = root;
while (p) {
if (p->val < val) {
if (p->right == NULL) {
p->right = t;
break;
} else {
p = p->right;
}
} else {
if (p->left == NULL) {
p->left = t;
break;
} else {
p = p->left;
}
}
}
return root;
}
};
2,有序数组构造二叉搜索树
转换的二叉搜索树可以有很多种,返回其中一种即可。思路:中间数字作为根节点,两边数字作为左右子树。
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return create(nums, 0, nums.size() - 1);
}
// 区间[l, r]
TreeNode* create(const vector<int>& nums, int l, int r) {
if (l > r) return NULL;
int mid = (l + r) / 2;
TreeNode *n = new TreeNode(nums[mid]);
n->left = create(nums, l, mid - 1);
n->right = create(nums, mid + 1, r);
return n;
}
};
3,前序遍历构造二叉搜索树
与题3不同,本题构造的二叉搜索树只有一种。思路:第1个节点为根,后面数字分成2部分,大于节点1的部分是右子树,其余为左子树。
class Solution {
public:
TreeNode* bstFromPreorder(vector<int>& preorder) {
return create(preorder, 0, preorder.size() - 1);
}
// 第1个节点为根,后面数字分成2部分,大于节点1的部分是右子树,其余为左子树
TreeNode* create(const vector<int>& nums, int l, int r) {
if (l > r) return NULL;
TreeNode *n = new TreeNode(nums[l]);
int mid = l + 1;
while (mid <= r) {
if (nums[mid] > nums[l]) break;
mid++;
}
n->left = create(nums, l+1, mid - 1);
n->right = create(nums, mid, r);
return n;
}
};
4,n个节点生成不同二叉搜索树的数量
来源:96. 不同的二叉搜索树
动态规划,将每个节点都作为根节点,左边数字作为左子树,右边数字作为右子树,可以组成树的数量有count(左子树) * count(右子树)。举例说明:
假如有5个数字:1,2,3,4,5,对于第4个数字,左子树有3个节点,可能情况为f(3),右子树有1个节点,可能情况为f(1)。
所以当4为根节点时,共有f(3)*f(1)种不同二叉搜索树。
依次类推:
1为根节点,数量:f(0)*f(4)
2为根节点,数量:f(1)*f(3)
3为根节点,数量:f(2)*f(2)
5为根节点,数量:f(4)*f(0)
当n=5时,数量:f(0)*f(4) + f(1)*f(3) + f(2)*f(2) + f(3)*f(1) + f(4)*f(0)。
所以n时,数量:f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-1)*f(0)
class Solution {
public:
// 动态规划,f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-1)*f(0)
int numTrees(int n) {
if (n <= 2) return n;
vector<int> f(n+1, 0);
f[0] = 1;
f[1] = 1;
f[2] = 2;
for (int i = 3; i <= n; i++) {
int s = 0;
for (int j = 0; j < i; j++) {
s += f[j] * f[i-j-1];
}
f[i] = s;
}
int ret = f[n];
return ret;
}
};
5,有序数组构造所有的二叉搜索树
与题2类似,不同的是,题2只需构造一种可能即可,本题需构造所有的可能,可能数量同题4。
思路1(举例说明):假如有数字序列:1,2,3,...,k,...,n,当根节点为k时,其左子树是节点1,2,3,,,k-1组成树的任意一种,右子树是k+1,...,n组成树的任意一种。
注意:左子树直接复制,右子树在复制的时候将节点累加k。
class Solution {
public:
vector<vector<TreeNode*>> dp;
vector<TreeNode*> generateTrees(int n) {
{
// n=0时,一颗空树
vector<TreeNode*> t(1, NULL);
dp.push_back(t);
}
{
// n=1时,一颗树且只有1个节点,且节点值为1
vector<TreeNode*> t(1, new TreeNode(1));
dp.push_back(t);
}
if (n <= 1) return dp[n];
for (int i = 2; i <= n; i++) {
generate(i);
}
return dp[n];
}
void generate(int k) {
vector<TreeNode*> result;
// 每个节点做根, 根节点j,左子树节点个数j-1, 右子树节点个数k-j-1
for (int j = 1; j <= k; j++) {
// dp[j-1] * dp[k-j]
const vector<TreeNode*>& left = dp[j-1];
const vector<TreeNode*>& right = dp[k-j];
for (int l = 0; l < left.size(); l++) {
for (int r = 0; r < right.size(); r++) {
TreeNode* root = new TreeNode(j);
root->left = copy(left[l], 0);
root->right = copy(right[r], j); // 右子树最小值应该为j+1,所以数值差j+1-1=j
result.push_back(root);
}
}
}
dp.push_back(result);
}
TreeNode* copy(TreeNode* root, int add_number) {
if (root == NULL) return NULL;
TreeNode *ret = new TreeNode(root->val + add_number);
ret->left = copy(root->left, add_number);
ret->right = copy(root->right, add_number);
return ret;
}
};
思路2:递归方案代码简单易懂,但不容易实现。遇到树的问题,应该先递归,递归实在想不出来,再考虑其他思路。
class Solution {
public:
vector<TreeNode*> generateTrees(int n) {
return generate(1, n);
}
vector<TreeNode*> generate(int start, int end) {
if (start > end) return vector<TreeNode*>(1, NULL);
vector<TreeNode*> result;
for (int i = start; i <= end; i++) {
// 递归生成左右子树
vector<TreeNode*> left = generate(start, i - 1);
vector<TreeNode*> right = generate(i + 1, end);
for (int l = 0; l < left.size(); l++) {
for (int r = 0; r < right.size(); r++) {
TreeNode* root = new TreeNode(i);
root->left = left[l];
root->right = right[r];
result.push_back(root);
}
}
}
return result;
}
};
6,二叉搜索树保留[low,high]的节点
来源:669. 修剪二叉搜索树
通过修剪二叉搜索树,使得所有节点的值在[low, high]
中,修剪树不应该改变保留在树中的元素的相对结构。
思路1:先前序遍历树得到范围内的所有节点,再将前序遍历结果转换为一颗二叉搜索树(题3)。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
vector<int> nums;
visit(root, low, high, &nums);
return create(nums, 0, nums.size() - 1);
}
void visit(TreeNode* root, int low, int high, vector<int> *ret) {
if (root == NULL) return;
if (root->val >= low && root->val <= high) ret->push_back(root->val);
visit(root->left, low, high, ret);
visit(root->right, low, high, ret);
}
TreeNode* create(const vector<int>& nums, int l, int r) {
if (l > r) return NULL;
TreeNode *n = new TreeNode(nums[l]);
int mid = l + 1;
while (mid <= r) {
if (nums[mid] > nums[l]) break;
mid++;
}
n->left = create(nums, l+1, mid - 1);
n->right = create(nums, mid, r);
return n;
}
};
思路2:当前节点如果比high大,说明当前节点及右子树均需要剪掉,在左子树上继续处理;如果比low小,在右子树上继续处理。如果在low和high范围内,左右分别处理。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (root == NULL) return NULL;
if (root->val > high) return trimBST(root->left, low, high);
if (root->val < low) return trimBST(root->right, low, high);
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
7,二叉搜索树删除某个节点
思路:先找到待删除节点,待删除节点有4种情况:
最后一种情况,也可以将右子树最小值min放到root,在右子树上删除min节点。结果不唯一,虽然与期望结果不一样,但依然可以通过。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == NULL) return NULL;
if (root->val > key) {
root->left = deleteNode(root->left, key);
} else if (root->val < key) {
root->right = deleteNode(root->right, key);
} else {
// 待删除节点
if (root->left == NULL) {
if (root->right == NULL) root = NULL; // 叶节点
else return root = root->right;
} else {
if (root->right == NULL) root = root->left;
else {
// 左子树最大值
TreeNode* p = root->left;
while (p->right) {
p = p->right;
}
root->val = p->val;
root->left = deleteNode(root->left, p->val);
}
}
}
return root;
}
};