Tag Archives:

LeetCode House Robber III

LeetCode House Robber III The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the “root.” Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that “all houses in this place forms a binary tree”. It will automatically contact the police if two directly-linked houses were broken into on the same night. Determine the maximum amount of money the thief can rob tonight without alerting the police. Example 1:

     3
    / \
   2   3
    \   \
     3   1
Maximum amount of money the thief can rob = 3 + 3 + 1 = 7. Example 2:
     3
    / \
   4   5
  / \   \
 1   3   1
Maximum amount of money the thief can rob = 4 + 5 = 9. Credits: Special thanks to @dietpepsi for adding this problem and creating all test cases.
这次要偷的房子分布在一棵二叉树上。。。 规则是有边相连的两个节点不能同时被偷,问最多能偷到多少钱。 我开始想的和LeetCode House Robber类似,一个节点要么偷,要么不偷。不偷得到的钱是上一个节点偷或不偷的最大值;偷得到的钱是上一个节点不偷加上该节点的钱数。使用递归的方法求解。 代码如下: [cpp] class Solution { private: void dfs(TreeNode* root, int& norobsum, int& robsum) { int nrs = max(norobsum, robsum); int rs = norobsum + root->val; norobsum = nrs; robsum = rs; if (root->left)dfs(root->left, norobsum, robsum); if (root->right)dfs(root->right, norobsum, robsum); } public: int rob(TreeNode* root) { if (!root)return 0; int norobsum = 0, robsum = 0; dfs(root, norobsum, robsum); return max(norobsum, robsum); } }; [/cpp] 本代码提交WA,错误样例如下:
     1
    / \
   2   3
令m[i]=(u,v)表示截至目前偷i节点得到u钱,不偷得到v钱。则m[1]=(1,0),递归进入左孩子,m[2]=(2+0,max(1,0))=(2,1),递归进入右孩子,m[3]=(3+1,max(2,1))=(4,2)。导致算出来的结果是4。这是因为3号节点偷的时候,其实不是3+1,1号节点的状态不能简单等价于2号节点的状态。 正确的做法应该是这样的,对于节点i,先算到左右节点偷或者不偷的钱数,即m[l]=(lrs,lnrs)和m[r]=(rrs,rnrs)。那么i偷的话,则左右节点都不能偷了=lnrs+rnrs+i.val;如果i不偷的话,则左右节点都可以偷或者不偷=max(lnrs,lrs)+max(rnrs,rrs),所以m[i]=(lnrs+rnrs+i.val, max(lnrs,lrs)+max(rnrs,rrs))。 代码如下: [cpp] class Solution { private: void dfs(TreeNode* root, int& norobsum, int& robsum) { int lnrs = 0, lrs = 0, rnrs = 0, rrs = 0; if (root->left)dfs(root->left, lnrs, lrs); if (root->right)dfs(root->right, rnrs, rrs); norobsum = max(lnrs, lrs) + max(rnrs, rrs); robsum = lnrs + rnrs + root->val; } public: int rob(TreeNode* root) { if (!root)return 0; int norobsum = 0, robsum = 0; dfs(root, norobsum, robsum); return max(norobsum, robsum); } }; [/cpp] 本代码提交AC,用时16MS。]]>

LeetCode Subtree of Another Tree

LeetCode Subtree of Another Tree Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node’s descendants. The tree s could also be considered as a subtree of itself. Example 1: Given tree s:

     3
    / \
   4   5
  / \
 1   2
Given tree t:
   4
  / \
 1   2
Return true, because t has the same structure and node values with a subtree of s. Example 2: Given tree s:
     3
    / \
   4   5
  / \
 1   2
    /
   0
Given tree t:
   4
  / \
 1   2
Return false.
判断二叉树t是否是二叉树s的子树。如果t是s从node0开始的子树,则t必须和node0子树完全一样,node0不能有多余的孩子,比如样例2中node0的子树多有一个节点0,所以是False。 首先写一个子程序check判断子树s和t是否相等,然后在主程序中使用先序遍历s,如果发现s的某个节点node0的值和t的根节点相等,则可以进入check(node0,t)判断node0子树是否和t相等。 代码如下: [cpp] class Solution { private: bool check(TreeNode* s, TreeNode* t) { if ((s == NULL && t != NULL) || (s != NULL&&t == NULL))return false; if (s == NULL && t == NULL)return true; //s!=NULL&&t!=NULL return (s->val == t->val) && check(s->left, t->left) && check(s->right, t->right); } public: bool isSubtree(TreeNode* s, TreeNode* t) { if (t == NULL)return true; if (s == NULL)return false; queue<TreeNode*> q; q.push(s); while (!q.empty()) { TreeNode *tmp = q.front(); q.pop(); if (tmp->val == t->val) { if (check(tmp, t))return true; } if (tmp->left != NULL)q.push(tmp->left); if (tmp->right != NULL)q.push(tmp->right); } return false; } }; [/cpp] 本代码提交AC,用时29MS。 还有一种更简洁漂亮的解法。如果t是s的子树,有三种情况,t要么和s完全相等check(s,t),要么等于s的某个左子树isSubtree(s->left,t),要么等于s的某个右子树isSubtree(s->right,t)。所以主程序和子程序都可以递归。 代码如下: [cpp] class Solution { private: bool check(TreeNode* s, TreeNode* t) { if ((s == NULL && t != NULL) || (s != NULL&&t == NULL))return false; if (s == NULL && t == NULL)return true; //s!=NULL&&t!=NULL return (s->val == t->val) && check(s->left, t->left) && check(s->right, t->right); } public: bool isSubtree(TreeNode* s, TreeNode* t) { if (t == NULL)return true; if (s == NULL)return false; if (check(s, t))return true; return isSubtree(s->left, t) || isSubtree(s->right, t); } }; [/cpp] 本代码提交AC,用时32MS。]]>

LeetCode Binary Tree Tilt

LeetCode Binary Tree Tilt Given a binary tree, return the tilt of the whole tree. The tilt of a tree node is defined as the absolute difference between the sum of all left subtree node values and the sum of all right subtree node values. Null node has tilt 0. The tilt of the whole tree is defined as the sum of all nodes’ tilt. Example:

Input:
         1
       /   \
      2     3
Output: 1
Explanation:
Tilt of node 2 : 0
Tilt of node 3 : 0
Tilt of node 1 : |2-3| = 1
Tilt of binary tree : 0 + 0 + 1 = 1
Note:
  1. The sum of node values in any subtree won’t exceed the range of 32-bit integer.
  2. All the tilt values won’t exceed the range of 32-bit integer.

定义二叉树一个节点的tilt为其左子树元素之和与右子树元素之和的差的绝对值。整棵二叉树的tilt为所有节点的tilt之和。现在要求一棵二叉树的tilt。 简单题,递归求解元素之和,然后更新tilt。都是递归到叶子节点,然后累加和往父亲节点走。 代码如下: [cpp] class Solution { private: void dfs(TreeNode* root, int& sum, int& tilt){ if(root == NULL) { sum = 0; tilt = 0; } else { int lsum = 0, rsum = 0, ltilt = 0, rtilt = 0; dfs(root->left, lsum, ltilt); dfs(root->right, rsum, rtilt); sum = lsum + rsum + root->val; tilt = ltilt + rtilt + abs(lsum – rsum); } } public: int findTilt(TreeNode* root) { int sum = 0, tilt = 0; dfs(root, sum, tilt); return tilt; } }; [/cpp] 本代码提交AC,用时16MS。]]>

hihoCoder 1507-可疑的记录

hihoCoder 1507-可疑的记录

#1507 : 可疑的记录

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi有一棵N个节点的树,编号1-N,其中1号节点是整棵树的根。他把这棵树的N-1条边记录成N-1行,每行2个整数a和b,表示a是b的父节点。 喜欢恶作剧的小Ho在小Hi的记录里加了一行两个整数,于是小Hi不得设法找出这行可疑的记录。具体来说,如果去掉某一行之后,余下N-1行按小Hi的规则(a是b的父节点)恰好能构成一棵N个节点的树,并且满足编号恰好是1-N且1号节点是根,那么被去掉的一行就被视为可疑的。 你能帮小Hi找出所有可疑的记录吗?

输入

第一行包含一个整数N,表示树的节点数目。 以下N行每行两个整数a和b,表示a是b的父节点。 对于30%的数据,1 <= N <= 1000 对于100%的数据, 1 <= N <= 100000, 1 <= a, b <= N 输入保证合法。

输出

输出一行包含若干个从小到大的整数,表示可疑的行号。(行号从1开始)
样例输入
3
1 2
1 3
1 3
样例输出
2 3

有一棵编号从1~N的树,其中根节点为1。这棵树的n-1条边用n-1行x y表示,其中x是y的父亲节点。现在小Ho在其中增加了一行,导致不满足上述性质,要求找出增加的那行。 这一题我用了BFS,DFS等乱七八糟的方法,最终WA。。。看过大神的代码之后,其实这题根本不需要用这些高深的策略,因为只添加了一行,所以用一些简单的规则即可找出。
  1. 首先,如果这一行x,y中y如果出现1,则这一行肯定是有问题的,因为1是根节点,不可能在右边。
  2. 其次,如果出现x==y,也有问题,因为树没有自回路。
  3. 最后,正常的树中除了根节点,每个节点有且仅有一个父亲节点,如果添加了一行,且不满足上面两种情况,则肯定会出现某个点的父亲节点有两个!所以我们只需要找出出现两个父亲节点的节点即可,而且这两个父亲都有可疑。
根据上述三条规则,马上写出代码: [cpp] #include<iostream> #include<cstdlib> #include<algorithm> #include<vector> #include<queue> #include<string> #include<unordered_map> #include<unordered_set> using namespace std; int n, x, y; int main() { //freopen("input.txt", "r", stdin); scanf("%d", &n); vector<int> xarry(n + 1, 0), yarry(n + 1, 0); for (int i = 0; i < n; ++i) { scanf("%d %d", &xarry[i], &yarry[i]); } vector<vector<int>> parent(n + 1, vector<int>()); for (int i = 0; i < n; ++i) { if (yarry[i] == 1) { // 根节点不可能在右边 printf("%d\n", i + 1); break; } if (xarry[i] == yarry[i]) { // 没有自回路 printf("%d\n", i + 1); break; } parent[yarry[i]].push_back(i + 1); } for (int i = 1; i <= n; ++i) { if (parent[i].size() > 1) { printf("%d %d ", parent[i][0], parent[i][1]); break; // 因为只添加了一行,所以只可能有一个节点有两个父亲 } } return 0; } [/cpp] 本代码提交AC,用时253MS。]]>

LeetCode Minimum Height Trees

LeetCode Minimum Height Trees For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels. Format The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels). You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. Example 1: Given n = 4, edges = [[1, 0], [1, 2], [1, 3]]

        0
        |
        1
       / \
      2   3
return [1] Example 2: Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
     0  1  2
      \ | /
        3
        |
        4
        |
        5
return [3, 4] Show Hint
    Note: (1) According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.” (2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
    给定一个无向无环图,问以哪个顶点为根形成的有根树的树高是最小的,如果有多个这样的最小高度的有根树,则输出所有这样的根节点。 很有意思的一题,需要把无向无环图转换为有根树。提示问这样的最小高度的有根树最多有多少个,我想了想,应该最多只有两个吧。可以用反证法,假设有3个,则这3个根肯定会形成环,具体原因大家可以在纸上画个图举个例子就知道了。 然后我由题目的样例想到的是,把图转换为有根的最小高度的树,根节点应该是从度数最高的前两个节点中选一个。所以我首先统计了所有节点的度数,然后选两个度数最高的节点,对这两个点DFS求以他们为根时的树高,如果树高相同,说明这两个点都是答案,否则取树高更高的节点作为根节点。 但是很可惜,代码提交之后WA,错误样例是这样的[[0,1],[0,2],[0,3],[2,4],[0,5],[5,6],[6,7],[2,8],[7,9]]。
    8     3
      \    \
        2----0----5----6----7----9
      /    /
    4    1
    在上图中,度数最多的前两个点是0和2,但是转换为有根树的最小高度的树根节点是5。所以我的解法是错误的。 参考网上的题解,好巧妙呀。这种解法类似于剥洋葱,一层一层把叶子节点剥离掉,最后剩下的2个或者1个节点就是最小树高的根节点。这种解法显然是正确的,没剥掉一层,相当于树高减一,一直减,到最后还剩下的节点肯定就是根节点了。比如上图中,第一层剥掉8,3,9,4,1,变成下图
    2----0----5----6----8
    第二层剥掉之后变成下图
    0----5----6
    第三层剥掉之后就只剩下5号节点了,所以以5号节点为根,树的高度最低,只有3,而我的解法算出来的根节点0的树高是4,不是最小的。
    5
    剥洋葱的解法用BFS来实现就非常方便了,BFS天然就是一层循环就是剥掉一层。 代码中,初始时,我们把图转换为类似邻接表的结果,但是每个vector里存的是unordered_set,而不是list,因为剥洋葱的时候,比如第一层时剥掉8号节点时,我们同时需要把2号节点的邻接表中的8号节点删掉,所以用unordered_set.erase就非常方便。后面循环时,没剥一层,我们就检查一下,把当前的叶子节点加入到队列中。当剩余的节点数不超过2个时,剩余的节点就是有根树的根节点。如果剩余两个节点,说明这两个节点的高度是一样的,都是正确答案。 代码如下: [cpp] class Solution { public: vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) { if (n == 1)return{ 0 }; int m = edges.size(); vector<unordered_set<int>> graph(n, unordered_set<int>()); for (int i = 0; i < m; ++i) { graph[edges[i].first].insert(edges[i].second); graph[edges[i].second].insert(edges[i].first); } queue<int> q; for (int i = 0; i < n; ++i) { if (graph[i].size() == 1) q.push(i); } while (n > 2) { int t = q.size(); n -= t; while (t–) { int leaf = q.front(); q.pop(); for (auto par : graph[leaf]) { // only one par graph[par].erase(leaf); if (graph[par].size() == 1)q.push(par); } } } vector<int> ans; while (!q.empty()) { ans.push_back(q.front()); q.pop(); } return ans; } }; [/cpp] 本代码提交AC,用时79MS。]]>

    LeetCode Verify Preorder Serialization of a Binary Tree

    LeetCode Verify Preorder Serialization of a Binary Tree One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, we record the node’s value. If it is a null node, we record using a sentinel value such as #.

         _9_
        /   \
       3     2
      / \   / \
     4   1  #  6
    / \ / \   / \
    # # # #   # #
    
    For example, the above binary tree can be serialized to the string "9,3,4,#,#,1,#,#,2,#,6,#,#", where # represents a null node. Given a string of comma separated values, verify whether it is a correct preorder traversal serialization of a binary tree. Find an algorithm without reconstructing the tree. Each comma separated value in the string must be either an integer or a character '#' representing null pointer. You may assume that the input format is always valid, for example it could never contain two consecutive commas such as "1,,3". Example 1: "9,3,4,#,#,1,#,#,2,#,6,#,#" Return true Example 2: "1,#" Return false Example 3: "9,#,#,1" Return false
    根据树的先序遍历结果,在不重构树的情况下,判断该先序遍历是否合法。 解法1,使用栈。因为先序遍历的顺序是根左右,如果我们遇到序列x,#,#,其中x不是#时,说明x的左右孩子都是#,此时,我们将x,#,#替换为#,表示将以x为根的子树替换为一个#。如此循环,如果栈中最终只剩下一个#,说明该序列合法。 比如样例”9,3,4,#,#,1,#,#,2,#,6,#,#”:
    • 9,3,4,#,# → 9,3,#
    • 9,3,#,1,#,# → 9,3,#,# → 9,#
    • 9,#,2,#,6,#,# → 9,#,2,#,# → 9,#,# → #
    不需要担心遇到x,#,#,#的情况,因为在遍历到第二个#时,就符合x,#,#的pattern了,会把x,#,#变成#。 代码如下: [cpp] class Solution { public: bool isValidSerialization(string preorder) { stack<string> stk; int i = 0, j = 0, n = preorder.size(); while (j < n) { while (j < n&&preorder[j] != ‘,’)++j; string node = preorder.substr(i, j – i); if (node == "#") { while (!stk.empty() && stk.top() == "#") { stk.pop(); if (stk.empty())return false; stk.pop(); } } stk.push(node); i = ++j; } return stk.size() == 1 && stk.top() == "#"; } }; [/cpp] 本代码提交AC,用时6MS。 解法2,利用度的信息。一棵二叉树通过题目中的加#策略之后,变成了一棵二叉树。这里的满是指一个节点要么有两个孩子,要么没有孩子,不存在有一个孩子的情况。(把#也看成孩子) 那么如果是叶子节点,则只贡献一个入度;如果是非叶子节点,则贡献一个入度和两个出度。所以diff=outdegree-indegree不可能是负数。 所以当来了一个节点,先–diff,因为这个节点肯定会贡献一个入度。然后判断这个节点如果是非叶子节点,则diff+=2,因为非叶子节点贡献两个出度。程序结束时,如果diff==0,则说明序列合法,否则不合法。因为就完整的树来说,入度肯定等于出度的,一条边对于父亲节点来说贡献出度,但对于孩子节点来说,贡献入度,所以总的入度和出度是相等的。 初始时,因为根节点没有入度,但是在外层while循环中,我们把根节点也看成普通的节点了。普通节点假设是有入度的,所以一进来就会–diff。所以初始时给根节点也补上一个入度,即diff=1,这样进入while之后,–diff不至于小于0。 代码如下: [cpp] class Solution { public: bool isValidSerialization(string preorder) { int i = 0, j = 0, n = preorder.size(), diff = 1; while (j < n) { while (j < n&&preorder[j] != ‘,’)++j; string node = preorder.substr(i, j – i); if (–diff < 0)return false; if (node != "#")diff += 2; i = ++j; } return diff == 0; } }; [/cpp] 本代码提交AC,用时3MS。 解法3,也是利用度的信息。前面说了,通过题目中的策略,一棵普通二叉树已经变成了一棵二叉树,满二叉树的特点是叶子节点的数目=非叶子节点的数目+1。简单证明如下: 假设叶子节点数为leaves,非叶子节点数为nonleaves。则总节点数为leaves+nonleaves,总边数就是leaves+nonleaves-1。一条边贡献两个度,所以总度数就是2(leaves+nonleaves-1)。从另一个角度,一个叶子节点贡献一个度,一个非叶子节点贡献3个度,根节点也是非叶子节点,但它只贡献两个读,所以总度数加起来就是leaves+3*nonleaves-1。令2(leaves+nonleaves-1)=leaves+3*nonleaves-1,就解出了leaves=nonleaves+1。 因为先序遍历是根左右,所以我们找一个满足leaves=nonleaves+1的最短前缀,如果这确实是一个合法的序列,则这个最短前缀就应该是序列本身。因为一棵满二叉树,在遍历的过程中是不会满足leaves=nonleaves+1的,只有当整棵树都遍历完之后才会满足这个条件。
         _9_
        /   \
       3     2
      / \   / \
     4   1  #  6
    / \ / \   / \
    # # # #   # #
    上面这棵树,虽然在遍历到节点2时,如果不再往下遍历了,看起来也是满二叉树,但是2是有孩子的,所以会被判断为nonleaves,所以还是不满足leaves=nonleaves+1。 代码如下: [cpp] class Solution3 { public: bool isValidSerialization(string preorder) { int leaves = 0, nonleaves = 0; int i = 0, j = 0, n = preorder.size(); while (j < n) { while (j < n&&preorder[j] != ‘,’)++j; string node = preorder.substr(i, j – i); if (node == "#")++leaves; else ++nonleaves; i = ++j; if (leaves – nonleaves == 1 && j < n)return false; } return leaves – nonleaves == 1; } }; [/cpp] 本代码提交AC,用时6MS。]]>

    LeetCode Count Complete Tree Nodes

    222. Count Complete Tree Nodes

    Given a complete binary tree, count the number of nodes.

    Note:

    Definition of a complete binary tree from Wikipedia:
    In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.

    Example:

    Input:      1    / \   2   3  / \  / 4  5 6 Output: 6

    计算完全二叉树的节点个数。如果忽略完全二叉树的性质,像普通二叉树那样进行遍历(先序、中序、后序都可以)统计节点个数,会TLE,所以还是需要用到完全二叉树的性质的。 这里的完全二叉树是指除了最后一层,其他层都是满的,并且最后一层所有节点都是靠左的。具体定义可以看维基百科,下图就是一个完全二叉树的例子: 如果二叉树是满的,比如下面这种,且树的高度是h,则树的节点个数等于$2^h-1$。 那么怎样判断一棵完全二叉树是否是满的呢,只需求一下其极左高度lh和极右高度rh是否相等,如果相等,则是满的,可以用上述公式求解节点数。如果不是满的,则在其左右子树中递归求解节点数,再加上根节点这个节点个数1。 代码如下:

    class Solution {
    public:
        int countNodes(TreeNode* root)
        {
            if (!root)
                return 0;
            int lh = 0, rh = 0;
            TreeNode *l = root, *r = root;
            while (l) {
                ++lh;
                l = l->left;
            }
            while (r) {
                ++rh;
                r = r->right;
            }
            if (lh == rh)
                return (1 << lh) – 1;
            return countNodes(root->left) + countNodes(root->right) + 1;
        }
    };

    本代码提交AC,用时82MS。
    以上代码还可优化,因为如果知道父亲节点的高度时,其实也就知道了孩子节点的高度了,就是父亲的高度减1,所以在递归时,可以保留高度信息,减少冗余计算。代码如下:

    class Solution {
    private:
        int height(TreeNode* root, int lh, int rh)
        {
            if (lh == -1) {
                lh = 0;
                TreeNode* l = root;
                while (l) {
                    ++lh;
                    l = l->left;
                }
            }
            if (rh == -1) {
                rh = 0;
                TreeNode* r = root;
                while (r) {
                    ++rh;
                    r = r->right;
                }
            }
            if (lh == rh)
                return (1 << lh) – 1;
            return height(root->left, lh – 1, -1) + height(root->right, -1, rh – 1) + 1;
        }
    public:
        int countNodes(TreeNode* root) { return height(root, -1, -1); }
    };

    本代码提交AC,用时55MS,快了一些。

    LeetCode Kth Smallest Element in a BST

    230. Kth Smallest Element in a BST

    Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.

    Example 1:

    Input: root = [3,1,4,null,2], k = 1
       3
      / \
     1   4
      \
       2
    Output: 1

    Example 2:

    Input: root = [5,3,6,2,4,null,null,1], k = 3
           5
          / \
         3   6
        / \
       2   4
      /
     1
    Output: 3
    

    Follow up:
    What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?

    Constraints:

    • The number of elements of the BST is between 1 to 10^4.
    • You may assume k is always valid, 1 ≤ k ≤ BST's total elements.

    给定一棵二叉搜索树,问树中第k小的元素是哪个。 因为BST的特点是左<=根<=右,所以对树中序遍历之后就得到升序序列,取出第k个元素即可。代码如下:

    class Solution {
    private:
        void inOrder(TreeNode* root, int& k, int& target)
        {
            if (k < 0)
                return;
            if (root->left)
                inOrder(root->left, k, target);
            –k;
            if (k == 0)
                target = root->val;
            if (root->right)
                inOrder(root->right, k, target);
        }
    public:
        int kthSmallest(TreeNode* root, int k)
        {
            int target;
            inOrder(root, k, target);
            return target;
        }
    };

    本代码提交AC,用时26MS,时间复杂度是O(k)。
    Follow up指出如果可以修改节点,怎样才能使得复杂度降低到O(height of tree),可以给每个节点增加一个属性rank:左子树节点个数,该属性的含义就是小于该节点的节点个数,也即当前节点在中序遍历中的位置(rank)。查找的过程就类似二分了,从根节点开始,如果k小于当前节点的rank,说明第k小的数在左子树;如果k大于当前节点的rank,说明第k小的数在右子树;如果k==rank,则当前节点就是第k小的数。这样的查找过程就是不断的在当前节点选择走左子树还是右子树,走过的节点最多是树的高度。
    rank属性可以在建BST时完成。

    LeetCode Most Frequent Subtree Sum

    LeetCode Most Frequent Subtree Sum Given the root of a tree, you are asked to find the most frequent subtree sum. The subtree sum of a node is defined as the sum of all the node values formed by the subtree rooted at that node (including the node itself). So what is the most frequent subtree sum value? If there is a tie, return all the values with the highest frequency in any order. Examples 1 Input:

      5
     /  \
    2   -3
    
    return [2, -3, 4], since all the values happen only once, return all of them in any order. Examples 2 Input:
      5
     /  \
    2   -5
    
    return [2], since 2 happens twice, however -5 only occur once. Note: You may assume the sum of values in any subtree is in the range of 32-bit signed integer.
    给定一棵二叉树,问频率最大的子树之和是哪个数。子树之和是指根节点及其子节点的和。 简单题,使用后序遍历把所有子树之和统计一遍,用Hash记录和与频率的关系,然后找出最大频率的和。 代码如下: [cpp] class Solution { private: int postOrder(TreeNode* root, unordered_map<int, int>& hash) { int sum = 0; if (root->left)sum += postOrder(root->left, hash); if (root->right)sum += postOrder(root->right, hash); sum += root->val; ++hash[sum]; return sum; } public: vector<int> findFrequentTreeSum(TreeNode* root) { if (!root)return vector<int>(); unordered_map<int, int> hash; postOrder(root, hash); int maxCnt = 0; for (auto it = hash.begin(); it != hash.end(); ++it)maxCnt = max(maxCnt, (*it).second); vector<int> ans; for (auto it = hash.begin(); it != hash.end(); ++it) { if ((*it).second == maxCnt)ans.push_back((*it).first); } return ans; } }; [/cpp] 本代码提交AC,用时19MS。]]>

    LeetCode Convert BST to Greater Tree

    LeetCode Convert BST to Greater Tree Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus sum of all keys greater than the original key in BST. Example:

    Input: The root of a Binary Search Tree like this:
                  5
                /   \
               2     13
    Output: The root of a Greater Tree like this:
                 18
                /   \
              20     13

    给定一棵二叉搜索树,要求把每个点的值换成BST中大于等于该点的其他所有点的值之和。 很有意思的一个题,因为BST满足左<=根<=右,则比某个点大的点在其根节点和右兄弟上,为了得到根节点和右兄弟节点之和,可以采用右->根->左的遍历顺序,也就是和先序遍历正好相反的顺序遍历BST,同时记录遍历过的数之和。 代码如下: [cpp] class Solution { private: void revPreOrder(TreeNode* root, int& sum) { if (root->right)revPreOrder(root->right, sum); sum += root->val; root->val = sum; if (root->left)revPreOrder(root->left, sum); } public: TreeNode* convertBST(TreeNode* root) { if (!root)return NULL; int sum = 0; revPreOrder(root, sum); return root; } }; [/cpp] 本代码提交AC,用时46MS。]]>