二叉搜索树的中序遍历:
二叉搜索的基本操作:
节点定义:
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {
}
};
1. 二叉搜索树的查找
bool BST_search(TreeNode* node, int value) {
if (node->val == value) {
return true;
}
if (node->val < value) {
if (node->left) {
return BST_search(node->left, value);
}
else {
return false;
}
}
else {
if (node->right) {
return BST_search(node->right, value);
}
else {
return false;
}
}
}
2. 二叉搜索树的节点插入
void BST_insert(TreeNode* node, TreeNode* insert_node) {
if (insert_node->val < node->val) {
if (node->left) {
BST_insert(node->left, insert_node);
}
else {
node->left = insert_node;
}
}
else {
if (node->right) {
BST_insert(node->right, insert_node);
}
else {
node->right = insert_node;
}
}
}
代码通过传入待插入节点的指针而不是在需要插入的地方new一个节点,这样做的好处是:将二叉树的维护和内存管理分开了。健壮的代码应该尽量弱化这两件事情的耦合关系。
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。(笔记:这个说明编码解码是有用的)
设计一个算法来序列化和反序列化二叉搜索树。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
注意:不要使用类成员/全局/静态变量来存储状态。 你的序列化和反序列化算法应该是无状态的
分析:
// 8
// / \
// 3 10
// / \ \
// 1 6 15
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string data;
BST_preorder(root, data);
return data;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if (data.size() == 0) {
return nullptr;
}
// string -> node
// "8#3#1#6#10#15#" -> TreeNode* node
vector<TreeNode*> node_vec;
int val = 0;
for (int i=0; i<data.size(); i++) {
if (data[i] == '#') {
node_vec.push_back(new TreeNode(val));
val = 0;
}
else {
// "15" -> 15
val = val * 10 + data[i] - '0';
}
}
// node -> BST
for (int i=1; i<node_vec.size(); i++) {
BST_insert(node_vec[0], node_vec[i]);
}
return node_vec[0];
}
private:
// 12 -> "12#"
void change_int_to_string(int val, string &str_val) {
string tmp;
// 将数字12 -> "21"
while (val) {
tmp += val%10 + '0';
val = val/10;
}
// "21" -> "12"
for (int i=tmp.size()-1; i>=0; i--) {
str_val += tmp[i];
}
// "12" -> "12#"
str_val += '#';
}
void BST_preorder(TreeNode* node, string &data) {
if (!node) {
return ;
}
string str_val;
change_int_to_string(node->val, str_val);
data += str_val;
BST_preorder(node->left, data);
BST_preorder(node->right, data);
}
// 二叉搜索树的节点插入程序(二叉搜索树的构建)
void BST_insert(TreeNode* root, TreeNode* node) {
if (node->val < root->val) {
if (root->left) {
BST_insert(root->left, node);
}
else {
root->left = node;
}
}
else {
if (root->right) {
BST_insert(root->right, node);
}
else {
root->right = node;
}
}
}
};
// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
本题在之前的递归小节笔记中5.2小节通过分治策略解决过:分治算法解决这个问题的核心思想是:
利用二叉搜索树任一节点的左子树数值都小于或等于根节点,右子树都大于根节点的性质,可以实现对nums中每个数的逆序数count_small的统计。
count_small:示例中5的count_small就是2,即5的右面有两个数比自己小。也叫逆序数;
方法如下1:
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
// 边界情况
if (nums.size() == 0) return vector<int>();
vector<int> ans;
// BSTNode节点池
vector<BSTNode*> node_vec;
// 逆置的nums中每个数对应的count_small值
vector<int> count_smalls;
// 从后往前创建BSTNode节点
for (int i=nums.size()-1; i>=0; i--) {
node_vec.push_back(new BSTNode(nums[i]));
}
// 边界条件:第一个节点的count_small值为0
count_smalls.push_back(0);
// 将非第一个节点插入到二叉搜索树中,并计算count_small值
for (int i=1; i<node_vec.size(); i++) {
// 插入节点的count_small值
int count_small = 0;
BST_insert(node_vec[0], node_vec[i], count_small);
count_smalls.push_back(count_small);
}
// 恢复节点顺序
for (int i=count_smalls.size()-1; i>=0; i--) {
ans.push_back(count_smalls[i]);
// 将外部不再使用的数据删除
delete node_vec[i];
}
return ans;
}
private:
// 带左子树节点数量count的节点定义
struct BSTNode {
int val;
int count;
BSTNode* left;
BSTNode* right;
BSTNode(int x): val(x), left(nullptr), right(nullptr), count(0) {
}
};
// 二叉搜索树的节点插入
void BST_insert(BSTNode* root, BSTNode* insert_ndoe, int &count_small) {
// 插入节点小于等于当前节点
if (insert_ndoe->val <= root->val) {
// 统计当前节点的左子树节点数目
root->count ++;
if (root->left) {
BST_insert(root->left, insert_ndoe, count_small);
}
else {
root->left = insert_ndoe;
}
}
else {
// 插入节点大于当前节点,则出现题目所求的count_small
// 因为本段代码的待插入节点的顺序是逆置后的nums
count_small += root->count + 1;
if (root->right) {
BST_insert(root->right, insert_ndoe, count_small);
}
else {
root->right = insert_ndoe;
}
}
}
};
https://www.bilibili.com/video/BV1GW411Q77S?p=6 ↩︎ ↩︎ ↩︎
https://leetcode-cn.com/problems/serialize-and-deserialize-bst/ ↩︎
https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/ ↩︎