剑指offer 打卡计划 | 每日进步一点点 | 第十二天

104 阅读3分钟

图片.png 一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

剑指 Offer 32 - II. 从上到下打印二叉树 II

思路

和上一题的代码一样,区别在于,每一层结束的时候,往queue里塞一个NULL做每一层的结尾标记。当我们访问到一层的结尾时,由于BFS的特点,我们刚好把下一层都加到了队列中。这个时候就可以给这层加上结尾标记NULL了。

c++代码

 /**
  * Definition for a binary tree node.
  * struct TreeNode {
  *     int val;
  *     TreeNode *left;
  *     TreeNode *right;
  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  * };
  */
 class Solution {
 public:
     vector<vector<int>> levelOrder(TreeNode* root) {
         vector<vector<int>> res; //记录答案
         if(!root) return res;
         vector<int> level; //记录一层节点
         queue<TreeNode*> q;
         q.push(root);
         q.push(NULL); //第一层结尾标记
         while(q.size()){
             auto t = q.front();
             q.pop();
             if(!t){  //遍历到当前层的结尾
                 if(level.empty()) break;
                 res.push_back(level);
                 q.push(NULL); //给下一层添加结尾标记
                 level.clear();
             }else{
                 level.push_back(t->val); //先将当层节点存贮到level,再将下一层存贮到队列中
                 if(t->left)   q.push(t->left);
                 if(t->right)  q.push(t->right);
             }
         }
         return res;
     }
 };

剑指 Offer 32 - III. 从上到下打印二叉树 III

思路

和上一题的思路类似,我们再加上一个flag标记,初始化flag = false,表示奇数行,如果是偶数行,flag = true,每次打印完一行后,将flag取反。

c++代码

 class Solution {
 public:
     // 宽度优先遍历
     vector<vector<int>> levelOrder(TreeNode* root) {
         vector<vector<int>> res; //记录答案
         queue<TreeNode*> q; //队列
         vector<int> level;  //存贮一层的节点
         bool flag = false;  //标记
         q.push(root);       //根节点入队
         q.push(NULL);       //一层的结尾标记
         while(q.size()){
             TreeNode* t = q.front();
             q.pop();
             if(!t){          //一层结束
                 if(!level.size())  break; // 最后一层结束
                 if(flag) reverse(level.begin(), level.end());
                 res.push_back(level);
                 flag = !flag;
                 level.clear();
                 q.push(NULL); //结尾标记入队
             }else{
                 level.push_back(t->val);        // 存贮当层节点
                 if(t->left)  q.push(t->left);   //下一层节点入队列
                 if(t->right) q.push(t->right);
             }
         }
         return res;
     }
 };

剑指 Offer 33. 二叉搜索树的后序遍历序列

思路

递归 O(n^2)

什么是二叉搜索树 ?

二叉搜索树是一棵有序的二叉树,所以我们也可以称它为二叉排序树。具有以下性质的二叉树我们称之为二叉搜索树:若它的左子树不为空,那么左子树上的所有值均小于它的根节点;若它的右子树不为空,那么右子树上所有值均大于它的根节点。它的左子树和右子树分别也为二叉搜索树。

二叉搜索树的后序遍历是左右根,先遍历左子树,然后右子树,最后根节点。

图片.png 判断是否是二叉搜索树的后序遍历序列:

  1. 递归当前序列的左右边界。
  2. 根节点是当前序列的最后一个元素。
  3. 遍历出左子树的终点在什么地方,这样我们就可以知道右子树的起始位置,然后我们遍历右子树的所有节点,看一下是否右子树的所有节点是否都比根节点要大,满足合法,否则不合法。
  4. 递归左右子树,只有左右子树同时合法,我们这棵树才是合法的。

如何遍历左子树的终点?

左子树的元素都小于根节点,这样我们从头遍历seq序列,通过该性质找到左右子树的分界点k,那么左子树序列为[l, k - 1],右子树序列为[k, r - 1]

时间复杂度分析: dfs中有个while循环,最坏情况下会循环 O(n) 次,一共会执行O(n) 次dfs,所以时间复杂度是 O(n^2)。

c++代码

 class Solution {
 public:
     bool verifyPostorder(vector<int>& post) {
         if(post.empty()) return true;
         return dfs(post, 0 ,post.size() - 1);
     }
 ​
     bool dfs(vector<int>& post, int l, int r){
         if(l >= r) return true;  //递归边界,只剩下一个数肯定满足
         int root = post[r]; //记录根节点
         int k = l;
         while(k < r && post[k] < root) k++; //先遍历出左子树的终点 k - 1
         for(int i = k; i <= r; i++){
             if(post[i] < root)  return false;
         }
         return dfs(post, l, k - 1) && dfs(post, k, r - 1);
     }
 };