
关键词:堆排序,优先队列,小根堆
这道题真没想出来怎么做,只能想到哈希统计数目,对优先队列还不是很熟悉,后来看了详解自己重写了一遍
主要思路是用哈希统计每个元素出现次数,再利用优先队列的性质创建小根堆(优先队列默认是从大到小排序,将是一个大根堆,因此需要重写比较函数cmp)
题目要求是前k个元素,因此我们维护一个大小为k的队列,每次插入元素队列自动排序将最小的排在堆顶,当数量>k时弹出堆顶元素,继续排序直到全部元素都遍历过
最后用vector存放堆中元素即可
这个与优先队列的底层代码有关,其他数据结构a.second>b.second表示的是从大到小,只需要记住优先队列是相反的就可以了

二叉树的后序遍历和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
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
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
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)

经典的bfs广度优先算法,这类题目都可以套模板做
这题本质上还是考察层序遍历,想到层序遍历就不难做, 只要把每一层最后一个元素取下来即可
- vector
int>> levelOrder(TreeNode* root) - {
- queue
que; - if(root)que.push(root);
- vector
int>> ans; - while(!que.empty())
- {
- int size=que.size();
- vector<int>res;
- for(int i=0;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()来挑选有叶子节点的子数
写递归不要用从头开始的思想写,从结果"归"的思想写更容易