LeetCode 热题 HOT 100 打卡计划 | 第十三天 | 每日进步一点点

127 阅读2分钟

图片.png

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

94. 二叉树的中序遍历

思路

思路1

(递归)

按照 左子树 => 根节点 => 右子树的顺序进行遍历二叉树。

c++代码1

 /**
  * Definition for a binary tree node.
  * struct TreeNode {
  *     int val;
  *     TreeNode *left;
  *     TreeNode *right;
  *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
  *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  * };
  */
 class Solution {
 public:
     vector<int> res;
     vector<int> inorderTraversal(TreeNode* root) {
         dfs(root);
         return res;
     }
 ​
     void dfs(TreeNode* root){
         if(!root) return ;
         dfs(root->left);
         res.push_back(root->val);
         dfs(root->right);
     }
 };

思路2

(迭代)

假设当前树的根节点为root,如果root != null,将整颗树的左链压入栈中。此时的栈顶元素就是我们想要的中序遍历结果,将其加入res中。如果有右子树,按照相同的步骤处理右子树。

c++代码2

 /**
  * Definition for a binary tree node.
  * struct TreeNode {
  *     int val;
  *     TreeNode *left;
  *     TreeNode *right;
  *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
  *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  * };
  */
 class Solution {
 public:
     vector<int> inorderTraversal(TreeNode* root) {
         vector<int> res;
         stack<TreeNode*> stk;
         while(root || stk.size()){
             while(root){  //将左子链压入栈中
                 stk.push(root);
                 root = root->left;
             }
 ​
             root = stk.top();
             stk.pop();
             res.push_back(root->val);  // 当前栈顶元素就是中序遍历的结果
             root = root->right; //处理右子树
         }
         return res;
     }
 };

96. 不同的二叉搜索树

思路

(动态规划) O(n^2)

状态表示: f[n]表示n个节点的二叉搜索树一共有多少种?

状态计算:

当给定的二叉树节点固定时,对应的二叉搜索树的情况数量也一定固定。对于节点数为i的二叉搜索树,我们去枚举二叉树的根节点的位置j,那么左子树区间为[1, j - 1],节点数为j - 1,右子树区间为[j + 1, i],节点数为[i - j]。将左子树长度和右子树的长度相乘就是节点数为i,根节点位置为j二叉树的种类,即f[i] = f[j - 1] * f[i - j]

而对于长度为n的二叉树,根据根节点位置的划分,左子树可以有 0,1,…n−1 个节点,对应的右子树有 n−1,n−2,…,0 个节点,f[n] 是所有这些情况的加和,所以f[n] = \sum_{k=0}^{n-1}f[k]*f[n-1-k] 。

初始化:

f[0]= 1,节点数为0的二叉树只有一种,为空树。

时间复杂度分析: 状态总共有 n 个,状态转移的复杂度是 O(n),所以总时间复杂度是 O(n^2)。

c++代码

 class Solution {
 public:
     int numTrees(int n) {
         vector<int>f(n + 1);
         f[0] = 1;
         for(int i = 1; i <= n; i++)  // 枚举二叉树的长度
             for(int j = 1; j <= i; j++) // 枚举根节点所在的位置
                 f[i] += f[j - 1] * f[i - j];  //左子树长度 * 右子树长度 (映射)
         return f[n];        
   }
 };

98. 验证二叉搜索树

思路

(深度优先遍历) O(n)

二叉搜索树是一种节点值之间具有一定数量级次序的二叉树,对于树中每个节点:

  • 若其左子树存在,则其左子树中每个节点的值都小于该节点值;
  • 若其右子树存在,则其右子树中每个节点的值都大于该节点值。

我们知道二叉搜索树**「中序遍历」**得到的值构成的序列一定是升序的的。因此我们可以对二叉树进行中序遍历,判断当前节点是否大于中序遍历的前一个节点,如果大于,说明说明这个序列是升序的,整棵树是二叉搜索树,否则不是。

二叉树的中序遍历顺序为: 左根右

图示:

图片.png

过程:

  • 1、我们定义一个节点变量pre用来记录中序遍历的前一个节点。
  • 2、中序遍历二叉树,在遍历过程中判断当前节点是否大于中序遍历的前一个节点。如果大于不做任何处理,如果小于等于说明不满足二叉搜索树的性质,返回false

细节:

  • 1、pre节点的初始值要设置为null
  • 2、具体实现过程看代码。

时间复杂度分析: 树中每个节点仅被遍历一遍,所以时间复杂度是 O(n)。

c++代码

 /**
  * Definition for a binary tree node.
  * struct TreeNode {
  *     int val;
  *     TreeNode *left;
  *     TreeNode *right;
  *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
  *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  * };
  */
 class Solution {
 public:
     TreeNode* pre = nullptr;
     bool isValidBST(TreeNode* root) {
         if(!root ) return true; //递归边界
         //判断当前节点的左子树是否满足二叉搜索树,如果不满足,直接返回false,无序递归右子树
         if(!isValidBST(root->left) )  return false; 
         //如果前一个节点不为空并且当前节点<=前一个节点,直接返回false
         if( pre && root->val <= pre->val) return false;
         //否则,将当前节点设置为pre
         pre = root;
         return isValidBST(root->right); //递归判断右子树
     }
 };
 ​