leetcode 347 前k个高频元素
关键词:堆排序,优先队列,小根堆
这道题真没想出来怎么做,只能想到哈希统计数目,对优先队列还不是很熟悉,后来看了详解自己重写了一遍
主要思路是用哈希统计每个元素出现次数,再利用优先队列的性质创建小根堆(优先队列默认是从大到小排序,将是一个大根堆,因此需要重写比较函数cmp)
题目要求是前k个元素,因此我们维护一个大小为k的队列,每次插入元素队列自动排序将最小的排在堆顶,当数量>k时弹出堆顶元素,继续排序直到全部元素都遍历过
最后用vector存放堆中元素即可
为什么创建小根堆是a.second>b.second?
这个与优先队列的底层代码有关,其他数据结构a.second>b.second表示的是从大到小,只需要记住优先队列是相反的就可以了
leetcode 145 二叉树的后序遍历
二叉树的后序遍历和144的前序遍历,94的中序遍历大同小异,这里就放一起当作一个题目来讲了
递归法
优点是代码简洁易写,但是效率差点,虽然时间复杂度都是O(n)
贴一个递归算法的代码,把递归顺序改一下就可以是其他遍历了
void traversal(TreeNode*node,vector<int>&v)
{
if(node==nullptr)
{
return;
}
traversal(node->left,v);
traversal(node->right,v);
v.emplace_back(node->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int>res;
traversal(root,res);
return res;
}
迭代法
递归是隐式地调用栈,而迭代法就是显式地调用栈,将调用栈的过程以代码呈现出来
前序遍历
也就是我们的说的根左右,因为根在前,所以我们在遍历到根的时候就可以把根处理了(结果数组中插入根的值,不着急弹出根因为后面还要从根的右子树进行遍历)
先将根入栈,然后一直其根的左子树入栈,直到左子树为空就换成在右子树遍历,右子树遍历完后将根弹出,完成一个树遍历
vector<int> preorderTraversal(TreeNode* root) {
vector<int>res;
stack<TreeNode*>Nodestack;
while(root!=nullptr||!Nodestack.empty())
{
while(root)
{
Nodestack.push(root);
res.emplace_back(root->val);
root=root->left;
}
root=Nodestack.top()->right;
Nodestack.pop();
}
return res;
}
后序遍历
后序是左右根,因此根要放到最后才处理(也就是说,根最后才加入结果数组)
先将根的左子树全部入栈,直到root为空,此时将栈顶(左边最后一个根)设为root,再遍历其右子树,如果右子树为空,则可以将根加入结果数组中了
vector<int> postorderTraversal(TreeNode* root) {
vector<int>res;
stack<TreeNode*>Nodestack;
TreeNode*pre=nullptr;
while(root!=nullptr||!Nodestack.empty())
{
while(root)
{
Nodestack.push(root);
root=root->left;
}
root=Nodestack.top();
Nodestack.pop();
if(root->right==nullptr||root->right==pre)
{
res.emplace_back(root->val);
pre=root;
root=nullptr;
}
else
{
Nodestack.push(root);
root=root->right;
}
}
return res;
}
当然也有一种方法,可以将前序的遍历顺序换一下再reverse结果数组,就可以将前序遍历变成后序遍历
中序遍历
中序遍历的算法和前序遍历差不多,不过是根的处理时间换了一下
先遍历完左子树,再将根处理,再遍历右子树
vector<int> inorderTraversal(TreeNode* root) {
vector<int>res;
stack<TreeNode*>Nodestack;
while(root!=nullptr||!Nodestack.empty())
{
while(root)
{
Nodestack.push(root);
root=root->left;
}
root=Nodestack.top();
res.emplace_back(root->val);
Nodestack.pop();
root=root->right;
}
return res;
}
统一遍历法
具体可看代码随想录 (programmercarl.com)
leetcode 199 二叉树的右视图
经典的bfs广度优先算法,这类题目都可以套模板做
这题本质上还是考察层序遍历,想到层序遍历就不难做, 只要把每一层最后一个元素取下来即可
层序遍历模板代码
vector<vector<int>> levelOrder(TreeNode* root)
{
queue<TreeNode*>que;
if(root)que.push(root);
vector<vector<int>> ans;
while(!que.empty())
{
int size=que.size();
vector<int>res;
for(int i=0;i<size;i++)
{
TreeNode*node=que.front();
que.pop();
res.push_back(node->val);
if(node->left){que.push(node->left);}
if(node->right){que.push(node->right);}
}
ans.emplace_back(res);
}
return ans;
}
leetcode 429 N叉树的层序遍历
这个是层序遍历的变式,改变的是子节点传入队列的方式,本质上还是层序遍历
Node类中包含了子节点的指针数组(代替了原来的左右子树),只要遍历数组就可以访问N个子节点,这样就完成了从二到N的转变
leetcode 111 二叉树的最小深度
这一题用层序遍历模板的方法做很容易能写出来,但递归的方法代码量少而且效率更高,这里讲一下
递归的方法
首先root为空的情况返回0就不用说了(确定递归的结束条件)
其次确定递归的参数是root节点的左右子树(确定参数)
最后是递归单层的处理(确定单层递归的逻辑)
单层递归:
我们将树的root节点分成三种情况:
root无子树 root只有一个子树 root有两个子树
惯性思维我们会将有子树和无子树进行分类然后写递归代码,但是这样写是错误的
两个子树的情况可以独立出来,用min()挑选左右子树中短的那一棵
有一个子树和无子树的情况是一样的,因为题目是求最小深度而不是最大深度,无子树是叶子节点,有一个子树的root并不是叶子节点,而我们写的代码很可能会用min()处理有一个子树的情况从而取到为空的子树(即算法找到最短的路径但不是叶子节点),因此我们要将一个子树的情况和无子树的情况当作同一种,用max()来挑选有叶子节点的子数
写递归不要用从头开始的思想写,从结果"归"的思想写更容易