求树的深度
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if(pRoot == NULL) return 0;
return max(TreeDepth(pRoot->left), TreeDepth(pRoot->right)) + 1;
}
};
判断平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树。
class Solution {
public:
int depth(TreeNode *root) {
if (!root) return 0; // 空树也是平衡树
int l = depth(root->left); // 提前截止
if (l == -1) return -1;
int r = depth(root->right); // 提前截止
if (r == -1) return -1;
if (abs(l - r)> 1) return -1;
return max(l, r) + 1;
}
bool IsBalanced_Solution(TreeNode* root) {
return depth(root) != -1;
}
};
重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路:
- 递归截止:子树序列中无结点
- 由前序序***定根结点
- 在中序序列中根据root的索引,分为左右子树
- 分别递归左子树和右子树
- 返回根节点
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
// 递归求解
return reconstruct(pre, 0, pre.size(), vin, 0, vin.size());
}
TreeNode* reconstruct(vector<int>& pre, int pl, int pr,
vector<int>& vin, int vl, int vr){
// 前序没有结点时,迭代终止
if(pl==pr) return nullptr;
// 根节点
TreeNode* root = new TreeNode(pre[pl]);
// 确定中序遍历中根节点的索引,分为左右子树,进行递归
for(int i=vl; i<vr; ++i){
if(vin[i] == root->val){
int num = i - vl; // 左子树的结点个数
root->left = reconstruct(pre, pl+1, pl+1+num, vin, vl, i);
root->right = reconstruct(pre, pl+1+num, pr, vin, i+1, vr);
break;
}
}
return root;
}
};
滑动窗口最大值
题目描述:
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
分析:
可以用暴力遍历的方法,时间复杂度:O(n*k), 其中n为数组大小,k为窗口大小。也可以用平衡二叉树或完全二叉树(大顶堆),这里只做试验:
平衡二叉树 map 自动排序,从小到大:将每个窗口的数据存入,删除老数据,存入新数据,读出最大的即可。
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
if (num.size() == 0 || size <= 0 || num.size() < size) return {};
vector<int> ret;
map<int, int> win; // 元素:索引
for (int i = 0; i < num.size(); ++i) {
if (win.size() < size) {
win[num[i]] = i;
if (size == win.size()) {
ret.push_back(win.rbegin()->first);
}
}
else {
win.erase(num[i - size]);
win[num[i]] = i;
ret.push_back(win.rbegin()->first);
}
}
return ret;
}
数据流的中位数
题目描述:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
分析:
(1) 最简单的:每次插入vector,取中位数时排序 std::sort( v.begin(), v.end() );
(2) 插入的同时,进行排序:low_bound
class Solution2 {
public:
void Insert(int num)
{
if (arr.empty()) arr.push_back(num);
else {
auto it = lower_bound(arr.begin(), arr.end(), num); // lower/upper都适用
arr.insert(it, num);
}
}
double GetMedian()
{
int n = arr.size();
return n & 1 ? arr[n / 2] : (arr[n / 2 - 1] + arr[n / 2]) * 0.5;
}
private:
vector<int> arr;
};
注意:(n>>2) - 1 移位运算符的优先级低于算术运算符!!!判断奇偶可以用位运算符
(3) 利用优先队列:大顶堆+小顶堆
// 大顶堆,用来存放中位数的较小数 lows
// 小顶堆,用来存放中位数的较大数 ups
- 最开始都是空堆,属于平衡状态
- lows中存进一个数,就要转给ups一个
- 如果 lows.size() < ups.size() 说明转多了,要补回来
class Solution3 {
public:
void Insert(int num)
{
lows.emplace(num);
ups.emplace(lows.top());
lows.pop();
if (lows.size() < ups.size()) {
lows.emplace(ups.top());
ups.pop();
}
}
double GetMedian()
{
return lows.size() > ups.size() ? lows.top() : (lows.top() + ups.top()) * 0.5;
}
private:
priority_queue<int> lows;// 大顶堆,用来存放中位数的较小数
priority_queue<int, vector<int>, greater<int>> ups; // 小顶堆,用来存放中位数的较大数
};
寻找第K个结点
题目描述:
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
分析:
这题比较难。二叉搜索树的中序遍历就是将结点从小到大排序,所以第1小的结点就是遍历左子树,一直到空。想到的就是递归,遍历到叶子结点后,第i次回溯就是第i小的结点,这时要考虑右子树。
增序: 1 2 3 4
降序: 4 3 2 1
所以找第k小,就是找 k-- == 1
class Solution {
public:
TreeNode* KthNode(TreeNode* pRoot, int k)
{
this->k = k;
dfs(pRoot);
return node;
}
void dfs(TreeNode* p){
// 节点不存在或已经找到则返回
if(!p || k<1) return;
// 继续寻找左子树
dfs(p->left);
// 回溯
if(k==1) node = p;
k--;
if(p->right) dfs(p->right);
}
private:
TreeNode* node = nullptr;
int k = 1;
};