61序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树。
- 序列化:发送数据时将结构化数据按照一定规则转换成字符串。
- 反序列化:接受到数据再按照相同的规则把字符串转化回原结构类型。
- 采用层序递归遍历将二叉树节点的值按层转换的字符型,并且添加到一个string中,以,分隔开来。遍历左右节点
- 递归出口是结点指向空,说明到了叶子节点,用‘#’标记叶子节点。
- 反序列化时候,采用找以,分隔开来的字符串,那原本是一个整数,将它还原成整数,构建节点,递归左右孩子。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
char* Serialize(TreeNode *root) {
if(root == NULL)
return NULL;
string str;
Serialize(root, str);
char *ret = new char[str.length() + 1];
strcpy(ret,str.c_str());
return ret;
}
void Serialize(TreeNode *root, string& str){
if(root == NULL){
str += '#';
return ;
}
string r = to_string(root->val);
str += r;
str += ',';
Serialize(root->left, str);
Serialize(root->right, str);
}
TreeNode* Deserialize(char *str) {
if(str == NULL)
return NULL;
TreeNode *ret = Deserialize(&str);
return ret;
}
TreeNode* Deserialize(char **str){//由于递归时,会不断的向后读取字符串
if(**str == '#'){ //所以一定要用**str,
++(*str); //以保证得到递归后指针str指向未被读取的字符
return NULL;
}
int num = 0;
while(**str != '\0' && **str != ','){
num = num*10 + ((**str) - '0');
++(*str);
}
TreeNode *root = new TreeNode(num);
if(**str == '\0')
return root;
else
(*str)++;
root->left = Deserialize(str);
root->right = Deserialize(str);
return root;
}
};
62二叉搜索树的第k个结点
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
- 二叉搜索树左子树值均比根节点值小,右子树值均比根节点大。从小到达的第k个节点,那么如果采用中序递归遍历,应该先找左子树,只要左子树存在,左子树值就是最小的。
- 左子树为空的节点,是此时第count的由小到大的节点,当count==k时候的当前节点就是目标节点。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
int count=0;
TreeNode* ret=nullptr;
TreeNode* KthNode(TreeNode* pRoot, int k)
{
if(pRoot!=nullptr){
if(pRoot->left!=nullptr)
ret=KthNode(pRoot->left,k);
if(ret!=nullptr)return ret;
count++;
if(count==k)return pRoot;
if(pRoot->right!=nullptr)
ret=KthNode(pRoot->right,k);
if(ret!=nullptr)return ret;
}
return nullptr;
}
};
63数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
1.采用vector存储数据流时候,sort排序是O(
n
∗
l
o
g
N
n*logN
n∗logN),插入效率是O(1)。
2. 获取中位数的效率是
O
(
n
∗
l
o
g
N
)
O(n*logN)
O(n∗logN),每一次获取中位数都需要sort排序。
class Solution {
public:
void Insert(int num)
{
number.push_back(num);
}
double GetMedian()
{
sort(number.begin(),number.end());
int size=number.size();
if(size%2==0)
return (double)((double)((number[size/2-1])+(number[size/2]))/2);
else
return (double)number[size/2];
}
private:
vector<int> number;
};
插入排序,插入数据时间效率 O ( n ) O(n) O(n),获取一个中位数的时间复杂度是 O ( 1 ) O(1) O(1)。
class Solution {
public:
vector<int> median;
void Insert(int num)
{
vector<int>::iterator pos=upper_bound(median.begin(),median.end(),num);
median.insert(pos,num);
}
double GetMedian()
{
if(median.size()==0) return 0.0;
int pos=median.size()/2;
if(median.size()%2==1){
return median[pos];
}
return (median[pos-1]+median[pos])/2.0;
}
};
实际上我们知道AVL树,这种十分平衡的二叉树搜索树来存储这样的数据流,效果很好,但是构造这样的结构很不划算。
- 我们可以用两个堆来代替AVL树:左子树用最大堆来实现,右子树用最小堆来实现。如上图所示,P1即为左子树最大堆堆顶,P2则为右子树最小堆堆顶。
- 当数组为偶数个时候,插入到最小堆,也就是右半部分,右半部分的数值应该都比左半部分的值大,但是插入的这个数比最大堆的堆顶数要小,怎么办?那么将这个数值更新到最大堆即可,因为我只关心最大堆的最大值和最小堆的最小值,不更新可能会影响这两个值的正确性。
- 获取中位数时候,我们只需判断当前数组是奇数个还是偶数个,偶数个就需要求两个堆顶的平均数,否则直接取小堆堆顶数。
class Solution {
private:
vector<int> min;
vector<int> max;
public:
void Insert(int num)
{
int size=min.size()+max.size();
if((size&1)==0)
{
if(max.size()>0 && num<max[0])
{
max.push_back(num);
push_heap(max.begin(),max.end(),less<int>());
num=max[0];
pop_heap(max.begin(),max.end(),less<int>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(),min.end(),greater<int>());
}
else
{
if(min.size()>0 && num>min[0])
{
min.push_back(num);
push_heap(min.begin(),min.end(),greater<int>());
num=min[0];
pop_heap(min.begin(),min.end(),greater<int>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(),max.end(),less<int>());
}
}
double GetMedian()
{
int size=min.size()+max.size();
if(size<=0)
return 0;
if((size&1)==0)
return (max[0]+min[0])/2.0;
else
return min[0];
}
};