leetcode刷题记录,有关树的中等难度算法题
 
  94. 二叉树的中序遍历 
给定一个二叉树,返回它的中序 遍历。 
 
1.非递归中序遍历 
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    vector <int > result;     vector <int > inorderTraversal (TreeNode* root)   {                          stack <TreeNode*> stack ;         TreeNode * cur = root;         while  (cur || !stack .empty()) {             while  (cur ) {                 stack .push(cur);                 cur = cur->left;             }             cur = stack .top();             stack .pop();             result.push_back(cur->val);             cur = cur->right;                      }         return  result;     }      }; 
 
2.递归中序遍历
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    vector <int > result;     vector <int > inorderTraversal (TreeNode* root)   {                return  result;     }     void  postorder (TreeNode *root)       {        if (!root) return ;         postorder(root->left);         result.push_back(root->val);         postorder(root->right);              } }; 
 
  95. 不同的二叉搜索树 II 
给定一个整数 n,生成所有由 1 … n 为节点所组成的二叉搜索树。 
 
我们从序列 1 …n 中取出数字 i,作为当前树的树根。于是,剩余 i - 1 个元素可用于左子树,n - i 个元素用于右子树。 
如 前文所述,这样会产生 G(i - 1) 种左子树 和 G(n - i) 种右子树,其中 G 是卡特兰数 
现在,我们对序列 1 … i - 1 重复上述过程,以构建所有的左子树;然后对 i + 1 … n 重复,以构建所有的右子树。 
这样,我们就有了树根 i 和可能的左子树、右子树的列表。 
最后一步,对两个列表循环,将左子树和右子树连接在根上。
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    vector <TreeNode*> generateTrees (int  n)   {         if (n == 0 )         {             vector <TreeNode *> a;             return  a;         }         return  generate_trees(1 , n);                       }     vector <TreeNode*> generate_trees (int  start,int  end)   {         vector <TreeNode*> all_trees;         if  (start > end) {             all_trees.push_back(NULL );             return  all_trees;         }         for (int  i = start ; i <=end ; i++)                      {             vector <TreeNode*> left_trees = generate_trees(start,i-1 );             vector <TreeNode*> right_trees = generate_trees(i+1 ,end);             for (int  l = 0  ; l <left_trees.size() ; l++)             {                 for (int  r = 0  ; r < right_trees.size() ; r ++)                 {                     TreeNode * current_tree = new  TreeNode(i);                     current_tree->left = left_trees[l];                     current_tree->right = right_trees[r];                     all_trees.push_back(current_tree);                 }             }         }         return  all_trees;     } }; 
 
  96. 不同的二叉搜索树(动态规划!= =!难顶) 
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种? 
 
动态规划  
给定一个有序序列 1 … n,为了根据序列构建一棵二叉搜索树。我们可以遍历每个数字 i,将该数字作为树根,1 … (i-1) 序列将成为左子树,(i+1) … n 序列将成为右子树。于是,我们可以递归地从子序列构建子树。
leetcode官方题解: 
 
 
 
class  Solution  {public :    int  numTrees (int  n)   {         vector <int > dp (n + 1 , 0 )  ;         dp[0 ] = 1 ;         dp[1 ] = 1 ;                  for (int  i = 2 ; i <= n; i++)             for (int  j = 1 ; j <= i; j++){                 dp[i] += dp[j - 1 ] * dp[i - j];             }         return  dp[n];     } }; 
 
  98. 验证二叉搜索树 
给定一个二叉树,判断其是否是一个有效的二叉搜索树。 
假设一个二叉搜索树具有如下特征: 
(1)节点的左子树只包含小于当前节点的数。 
(2)节点的右子树只包含大于当前节点的数。 
(3)所有左子树和右子树自身必须也是二叉搜索树。
 
 
注意(官方题解提示):  
1.递归判断  
首先将结点的值与上界和下界(如果有)比较。然后,对左子树和右子树递归进行该过程。
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    bool  isValidBST (TreeNode* root)   {         return  judge(root, LONG_MIN,LONG_MAX);     }     bool  judge (TreeNode * root, long  low, long   up)       {        if (root == nullptr ) return  true ;         long  val = root->val;         if (val<=low || val>=up) return  false ;         return  judge(root->left,low,val) &&judge(root->right,val,up);     }      }; 
 
2.中序遍历  
左子树 -> 结点 -> 右子树 
对于二叉搜索树而言,每个元素都应该比下一个元素小
class  Solution  {public :    bool  isValidBST (TreeNode* root)   {         stack <TreeNode *> s;         while  (root !=NULL  || !s.empty()) {             while  (root != NULL ) {                 s.push(root);                 root = root->left;             }             root = s.top();             s.pop();             if  (root->val <= min) return  false ;             min = root->val;             root = root->right;         }                          return  true ;          }   }; 
 
  113. 路径总和 II 
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 
说明: 叶子节点是指没有子节点的节点。 
 
按照递归的方法依次遍历左右子树各个结点。 
如果到叶节点则判断是否路径要求。 
利用回溯思想将每次目标路径的数组元素回退到上一个结点,之后遍历另一条边的结点寻找更多的可能性 
 
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    vector <vector <int >> result;     vector <vector <int >> pathSum(TreeNode* root, int  sum) {         vector <int > path;         dfs(root,sum,path);         return  result;     }     void  dfs (TreeNode* root, int  sum,vector <int > & path)       {        if (!root) return ;         path.push_back(root->val);                  if (!root->left && !root->right)         {             int  s = 0 ;             for (int  i = path.size()-1  ; i>= 0  ; i--)             {                 s+= path[i];                              }             if (s == sum)             {                 result.push_back(path);             }         }                  dfs(root->left, sum, path);         dfs(root->right, sum, path);         path.pop_back();                       } }; 
 
  129. 求根到叶子节点数字之和 
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。 
 
对树进行DFS,没到一个不是叶节点的新节点就将其组成数字,到叶节点则加到总和sum中
struct  TreeNode  {     int  val;      TreeNode *left;      TreeNode *right;      TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {}  }; class  Solution  {public :    int  sum = 0 ;          int  sumNumbers (TreeNode* root)   {         dfs(root, 0 );         return  sum;     }     void  dfs (TreeNode* root,int  x)       {        if (!root)return ;         x = x*10 +root->val;         if (!root->left && !root->right)         {             sum +=x;                      }         dfs(root->left, x);         dfs(root->right, x);     } }; 
 
  144. 二叉树的前序遍历 
 
1.递归 
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    vector <int > result;     vector <int > preorderTraversal (TreeNode* root)   {                 preorderorder(root);     }     void  preorderorder (TreeNode *root)       {        if (!root) return ;         result.push_back(root->val);         preorderorder(root->left);         preorderorder(root->right);     } }; 
 
2.非递归  
创建一个Stack用来存放节点 
首先我们想要打印根节点的数据,此时Stack里面的内容为空,所以我们优先将头结点加入Stack,然后打印。 
之后我们应该先打印左子树,然后右子树。所以先加入Stack的就是右子树,然后左子树。
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    vector <int > result;     vector <int > preorderTraversal (TreeNode* root)   {                  if (!root)         {             return  result;         }         stack <TreeNode*> stack ;                  stack .push(root);         while  ( !stack .empty()) {             TreeNode * node = stack .top();             stack .pop();             result.push_back(node->val);             if  (node->right ) {                 stack .push(node->right);             }             if  (node->left ) {                 stack .push(node->left);             }                                                }         return  result;     }      }; 
 
  684. 冗余连接 
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 
的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式u < v。 
 
1.并查集  
(1)初始化 
刚开始每个元素都是一个独立的集合,需要令所有的father[i]=i;
(2)查找 
find(node x),找到元素 x 所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合。 
实现的方式可以是递归或者迭代,思路都是反复寻找父亲节点,直到根节点 
(3)合并 
union(node x, node y),把元素 x 和元素 y 所在的集合合并,要求 x和 y 所在的集合不相交,如果相交则不合并。 
合并的过程是将一个集合的根节点的父亲指向另一个集合的根结点
class  Solution  {public :    int  parent[1001 ];     int  findFather (int  x)       {    		         while  (x !=findFather(x)) {             parent[x] = parent[parent[x]];             x = parent[x];         }         return  x;     }     bool  union_root (int  x, int  y)   {         int  root_x = findFather(x);         int  root_y = findFather(y);                  if  (root_x == root_y) {             return  false ;         }                   parent[root_x] = root_y;         return  true ;     }               vector <int > findRedundantConnection (vector <vector <int >>& edges)   {              for (int  i = 0  ; i< 1001 ; i ++)         {             parent[i] = i;                      }         for  (auto  edge : edges) {             if  (!union_root(edge[0 ], edge[1 ])) {                 return  edge;             }         }         return  {};                       }      }; 
 
2.DFS  
对于每个边 (u, v),用深度优先搜索遍历图,以查看是否可以将 u 连接到 v。如果可以,则它是重复边
  988. 从叶结点开始的最小字符串 
给定一颗根结点为 root 的二叉树,树中的每一个结点都有一个从 0 到 25 的值,分别代表字母 ‘a’ 到 ‘z’:值 0 代表 
‘a’,值 1 代表 ‘b’,依此类推。
找出按字典序最小的字符串,该字符串从这棵树的一个叶结点开始,到根结点结束。
(小贴士:字符串中任何较短的前缀在字典序上都是较小的:例如,在字典序上 “ab” 比 “aba” 要小。叶结点是指没有子结点的结点。) 
 
 
暴力破解: 
创建出所有可能的字符串,然后逐一比较,输出字典序最小的那个。 
在我们深度优先搜索的过程中,不断调整根到这个节点的路径上的字符串内容。 
当我们到达一个叶子节点的时候,我们翻转路径的字符串内容来创建一个候选答案。如果候选答案比当前答案要优秀,那么我们更新答案。
struct  TreeNode  {    int  val;     TreeNode *left;     TreeNode *right;     TreeNode(int  x) : val(x), left(NULL ), right(NULL ) {} }; class  Solution  {public :    string  ans = "z" ;     string  smallestFromLeaf (TreeNode* root)   {         dfs(root, "" );         return  ans;     }     void  reverse (string  & s)       {        int  len = s.size()-1 ;         int  mid = len/2 ;         int  i = 0 ;         while (i<=mid){             char  temp = s[i];             s[i] = s[len-i];             s[len-i] = temp;             i++;    }     }     void  dfs (TreeNode* root,string  s)       {        if (!root) return ;         s +=char ('a' +root->val);                  if (!root->left && !root->right)         {             reverse(s);             if (s<ans)             {                 ans = s;  }         }         dfs(root->left, s);         dfs(root->right, s);     } };