Tag Archives: BFS

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 Evaluate Division

    LeetCode Evaluate Division Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number (floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0. Example: Given a / b = 2.0, b / c = 3.0. queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? . return [6.0, 0.5, -1.0, 1.0, -1.0 ]. The input is: vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries , where equations.size() == values.size(), and the values are positive. This represents the equations. Return vector<double>. According to the example above:

    equations = [ ["a", "b"], ["b", "c"] ],
    values = [2.0, 3.0],
    queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ].
    The input is always valid. You may assume that evaluating the queries will result in no division by zero and there is no contradiction.
    给定一系列的除法表达式,要计算另一些除法表达式的结果。比如知道a/b=2.0,b/c=3.0,问a/c=?。 本题的解法是把一系列表达式转换为图,比如a/b=2.0转换为a指向b的边,边的值为2.0,同时b指向a的边的值为1/2.0。如果要求a/c,则在图中找一条a到c的边,并且把走过的边的值乘起来。 构建图的过程先把表达式中的string编码成int,然后把已知的直连边加入到图中。对于要查询的边start/target,首先判断一下两个端点是否在图中,如果至少有一个端点不在图中,则无法求值,返回-1。否则,在图中DFS或BFS找到target,走过的路径乘积就是结果。 题目说明所有values都是正数,所以不存在除0问题。 DFS代码如下: [cpp] class Solution { private: bool dfs(vector<vector<double>>& graph, int start, int x, int y, int target) { graph[start][y] = graph[start][x] * graph[x][y]; graph[y][start] = 1.0 / graph[start][y]; if(y == target)return true; int n = graph.size(); for(int i = 0; i < n; ++i){ if(graph[start][i] == 0 && graph[y][i] != 0){ if(dfs(graph, start, y, i, target))return true; } } return false; } double helper(vector<vector<double>>& graph, int start, int target) { if (start == target)return 1.0; else if (graph[start][target] != 0.0)return graph[start][target]; int n = graph.size(); for (int y = 0; y < n; ++y) { if (y == start)continue; if (graph[start][y] != 0.0) { if (dfs(graph, start, start, y, target))return graph[start][target]; } } return -1.0; } public: vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) { int cnt = 0, n = equations.size(); unordered_map<string, int> hash; for (int i = 0; i < n; ++i) { if (hash.find(equations[i].first) == hash.end())hash[equations[i].first] = cnt++; if (hash.find(equations[i].second) == hash.end())hash[equations[i].second] = cnt++; } vector<vector<double>> graph(cnt, vector<double>(cnt, 0)); for (int i = 0; i < n; ++i) { int x = hash[equations[i].first], y = hash[equations[i].second]; graph[x][x] = 1; graph[y][y] = 1; graph[x][y] = values[i]; graph[y][x] = 1.0 / values[i]; } vector<double> ans; for (int i = 0; i < queries.size(); ++i) { if (hash.find(queries[i].first) == hash.end() || hash.find(queries[i].second) == hash.end())ans.push_back(-1.0); else { int x = hash[queries[i].first], y = hash[queries[i].second]; ans.push_back(helper(graph, x, y)); } } return ans; } }; [/cpp] 本代码提交AC,用时3MS。 BFS代码如下: [cpp] class Solution { private: double bfs(vector<vector<double>>& graph, int start, int target) { if (start == target)return 1.0; else if (graph[start][target] != 0.0)return graph[start][target]; int n = graph.size(); queue<int> q; for(int x = 0; x < n; ++x){ if(x != start && graph[start][x] != 0)q.push(x); } while(!q.empty()){ int x = q.front(); if(x == target) return graph[start][target]; q.pop(); for(int y = 0; y < n; ++y){ if(graph[start][y] == 0 && graph[x][y] != 0){ graph[start][y] = graph[start][x] * graph[x][y]; graph[y][start] = 1.0 / graph[start][y]; q.push(y); } } } return -1.0; } public: vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) { int cnt = 0, n = equations.size(); unordered_map<string, int> hash; for (int i = 0; i < n; ++i) { if (hash.find(equations[i].first) == hash.end())hash[equations[i].first] = cnt++; if (hash.find(equations[i].second) == hash.end())hash[equations[i].second] = cnt++; } vector<vector<double>> graph(cnt, vector<double>(cnt, 0)); for (int i = 0; i < n; ++i) { int x = hash[equations[i].first], y = hash[equations[i].second]; graph[x][x] = 1; graph[y][y] = 1; graph[x][y] = values[i]; graph[y][x] = 1.0 / values[i]; } vector<double> ans; for (int i = 0; i < queries.size(); ++i) { if (hash.find(queries[i].first) == hash.end() || hash.find(queries[i].second) == hash.end())ans.push_back(-1.0); else { int x = hash[queries[i].first], y = hash[queries[i].second]; ans.push_back(bfs(graph, x, y)); } } return ans; } }; [/cpp] 本代码提交AC,用时0MS,类似的题BFS显然要比DFS快,而且这题用BFS思路更清晰。]]>

    LeetCode 01 Matrix

    LeetCode 01 Matrix Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell. The distance between two adjacent cells is 1. Example 1: Input:

    0 0 0
    0 1 0
    0 0 0
    
    Output:
    0 0 0
    0 1 0
    0 0 0
    
    Example 2: Input:
    0 0 0
    0 1 0
    1 1 1
    
    Output:
    0 0 0
    0 1 0
    1 2 1
    
    Note:
    1. The number of elements of the given matrix will not exceed 10,000.
    2. There are at least one 0 in the given matrix.
    3. The cells are adjacent in only four directions: up, down, left and right.

    给定一个0/1矩阵,要求每个点到其最近的0的距离。显然,如果自身是0的话,到最近的0的距离就是0,所以这里只需要求当cell不为0时,它到最近的0的距离。 显然是个BFS的题,从点(i,j)往外广度搜索,第一个遇到0时,走过的步数就是和0的最短距离;没遇到0时,不断的把点加入到队列中。 自定义一个MyPoint结构体,保存点的坐标,以及从出发点到该点的步数。注意本题不需要visited数组,visited数组的目的是保证在BFS时不走之前走过的点,但是BFS走过的路径肯定有那种没走之前走过的点的路径,这样的路径到达0的距离肯定会比走重复点的路径到达0的距离要短。所以不用visited也能找到最短距离。 代码如下: [cpp] typedef struct MyPoint { int x, y; int step; MyPoint(int x_, int y_, int step_) :x(x_), y(y_), step(step_) {}; }; class Solution { private: int bfs(int i, int j, vector<vector<int>>& matrix) { int m = matrix.size(), n = matrix[0].size(); MyPoint p(i, j, 0); queue<MyPoint> qp; qp.push(p); while (!qp.empty()) { p = qp.front(); if (matrix[p.x][p.y] == 0)return p.step; qp.pop(); if (p.x – 1 >= 0) { MyPoint tmp(p.x – 1, p.y, p.step + 1); qp.push(tmp); } if (p.x + 1 < m) { MyPoint tmp(p.x + 1, p.y, p.step + 1); qp.push(tmp); } if (p.y – 1 >= 0) { MyPoint tmp(p.x, p.y – 1, p.step + 1); qp.push(tmp); } if (p.y + 1 < n) { MyPoint tmp(p.x, p.y + 1, p.step + 1); qp.push(tmp); } } } public: vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) { int m = matrix.size(), n = 0; if (m > 0)n = matrix[0].size(); if (m == 0 || n == 0)return vector<vector<int>>(); vector<vector<int>> ans(m, vector<int>(n, 0)); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (matrix[i][j] != 0) { ans[i][j] = bfs(i, j, matrix); } } } return ans; } }; [/cpp] 本代码提交AC,用时276MS。]]>

    LeetCode Find Largest Value in Each Tree Row

    LeetCode Find Largest Value in Each Tree Row You need to find the largest value in each row of a binary tree. Example:

    Input:
              1
             / \
            3   2
           / \   \
          5   3   9
    Output: [1, 3, 9]

    给定一棵二叉树,找出每层的最大值。 简单题,直接BFS,在每一层找最大值。代码如下: [cpp] class Solution { public: vector<int> largestValues(TreeNode* root) { vector<int> ans; if (!root)return ans; queue<TreeNode*> q; q.push(root); while (!q.empty()) { int largest = INT_MIN, n = q.size(); while (n–) { TreeNode* front = q.front(); q.pop(); largest = max(largest, front->val); if (front->left)q.push(front->left); if (front->right)q.push(front->right); } ans.push_back(largest); } return ans; } }; [/cpp] 本代码提交AC,用时13MS。]]>

    LeetCode Find Bottom Left Tree Value

    LeetCode Find Bottom Left Tree Value Given a binary tree, find the leftmost value in the last row of the tree. Example 1:

    Input:
        2
       / \
      1   3
    Output:
    1
    
    Example 2:
    Input:
            1
           / \
          2   3
         /   / \
        4   5   6
           /
          7
    Output:
    7
    
    Note: You may assume the tree (i.e., the given root node) is not NULL.
    找出一棵二叉树最后一行的最左边元素。 简单题,直接BFS,每层保存队列第一个元素,BFS结束之后,就能得到最后一行的第一个元素。 代码如下: [cpp] class Solution { public: int findBottomLeftValue(TreeNode* root) { queue<TreeNode*> q; q.push(root); int ans = 0; while (!q.empty()) { int n = q.size(); ans = q.front()->val; while (n–) { TreeNode* front = q.front(); q.pop(); if (front->left)q.push(front->left); if (front->right)q.push(front->right); } } return ans; } }; [/cpp] 本代码提交AC,用时12MS。]]>

    LeetCode Binary Tree Zigzag Level Order Traversal

    Given a binary tree, return the zigzag level order traversal of its nodes’ values. (ie, from left to right, then right to left for the next level and alternate between).

    For example:
    Given binary tree [3,9,20,null,null,15,7],

        3
       / \
      9  20
        /  \
       15   7
    

    return its zigzag level order traversal as:

    [
      [3],
      [20,9],
      [15,7]
    ]
    

    本题还是树的层次遍历,但是稍微有点不一样,要求奇数层从左往右输出,偶数层从右往左输出。常规的层次遍历使用BFS实现,即队列,这里要求颠倒顺序,马上想到用堆栈。
    维护一个left2right布尔变量,当tru时,把孩子从左往右压栈,这样下一层的输出顺序就是从右往左了;当为false时相反。这里我们还需要用到两个堆栈,分别保存当前层和下一层的结果。
    talk is cheap, show you the code!

    class Solution {
    public:
        vector<vector<int> > zigzagLevelOrder(TreeNode* root)
        {
            vector<vector<int> > ans;
            if (!root)
                return ans;
            bool left2right = true;
            stack<TreeNode*> stk;
            stk.push(root);
            while (!stk.empty()) {
                stack<TreeNode*> stk2;
                vector<int> cand;
                size_t n = stk.size();
                for (size_t i = 0; i < n; ++i) {
                    TreeNode* top = stk.top();
                    stk.pop();
                    if (!top)
                        continue;
                    cand.push_back(top->val);
                    if (left2right) {
                        stk2.push(top->left);
                        stk2.push(top->right);
                    }
                    else {
                        stk2.push(top->right);
                        stk2.push(top->left);
                    }
                }
                left2right = !left2right;
                if (!cand.empty())
                    ans.push_back(cand);
                stk = stk2;
            }
            return ans;
        }
    };

    本代码提交AC,用时6MS。

    二刷。不使用两个栈,而使用一个双端队列也可以:

    class Solution {
    public:
    	vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
    		vector<vector<int>> ans;
    		if (root == NULL)return ans;
    		deque<TreeNode*> q;
    		q.push_back(root);
    		bool left2right = true;
    		while (!q.empty()) {
    			vector<int> cur;
    			if (left2right) {
    				int n = q.size();
    				for (int i = 0; i < n; ++i) {
    					cur.push_back(q[i]->val);
    				}
    				ans.push_back(cur);
    				for (int i = n - 1; i >= 0; --i) {
    					TreeNode* tn = q.back();
    					q.pop_back();
    					if (tn->right)q.push_front(tn->right);
    					if (tn->left)q.push_front(tn->left);
    				}
    				left2right = false;
    			}
    			else {
    				int n = q.size();
    				for (int i = n - 1; i >= 0; --i) {
    					cur.push_back(q[i]->val);
    				}
    				ans.push_back(cur);
    				for (int i = 0; i < n; ++i) {
    					TreeNode* tn = q.front();
    					q.pop_front();
    					if (tn->left)q.push_back(tn->left);
    					if (tn->right)q.push_back(tn->right);
    				}
    				left2right = true;
    			}
    		}
    		return ans;
    	}
    };

    本代码提交AC,用时4MS。

    LeetCode Word Ladder

    127. Word Ladder

    Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

    1. Only one letter can be changed at a time.
    2. Each transformed word must exist in the word list.

    Note:

    • Return 0 if there is no such transformation sequence.
    • All words have the same length.
    • All words contain only lowercase alphabetic characters.
    • You may assume no duplicates in the word list.
    • You may assume beginWord and endWord are non-empty and are not the same.

    Example 1:

    Input:
    beginWord = "hit",
    endWord = "cog",
    wordList = ["hot","dot","dog","lot","log","cog"]
    
    Output: 5
    
    Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
    return its length 5.
    

    Example 2:

    Input:
    beginWord = "hit"
    endWord = "cog"
    wordList = ["hot","dot","dog","lot","log"]
    
    Output: 0
    
    Explanation: The endWord "cog" is not in wordList, therefore no possibletransformation.

    很有意思的一个题。给定两个单词beginWord和endWord,以及一个字典数组,问从beginWord到endWord,最少需要多少次变换,每次只能变换一个字母,而且变换的单词都必须存在于字典数组中。

    很明显是一个BFS题,从beginWord开始广搜,直到第一次搜到endWord时停止,此时走过的变换数就是最小变换数。因为广搜会搜索所有的空间,那么第一次到达endWord也就是最早到达的。
    当然BFS的题也都能用DFS来做,但是DFS显然会慢,想想便知,DFS必须搜索完一条完整的路径才能知道结果,才能进入下一条路径开始搜索;而BFS是每条路径同步进行,只要有一条路径到达,就结束搜索。
    常规的BFS代码如下:

    typedef struct Item {
        string cur;
        string used;
        int step;
        Item(string c, string u, int s)
            : cur(c)
            , used(u)
            , step(s){};
    };
    class Solution {
    public:
        bool differOne(const string& s1, const string& s2)
        {
            int cnt = 0;
            for (size_t i = 0; i < s1.size(); ++i) {
                if (s1[i] != s2[i]) {
                    ++cnt;
                    if (cnt > 1)
                        return false;
                }
            }
            return cnt == 1;
        }
        int ladderLength(string beginWord, string endWord, vector<string>& wordList)
        {
            size_t n = wordList.size();
            queue<Item> q;
            Item it(beginWord, string(n, ‘0’), 1);
            q.push(it);
            while (!q.empty()) {
                Item t = q.front();
                q.pop();
                if (t.cur == endWord)
                    return t.step;
                for (size_t i = 0; i < n; ++i) {
                    if (t.used[i] == ‘0’&& differOne(t.cur, wordList[i])) {
                        string newUsed = t.used;
                        newUsed[i] = ‘1’;
                        Item tmp(wordList[i], newUsed, t.step + 1);
                        q.push(tmp);
                    }
                }
            }
            return 0;
        }
    };

    本代码很好理解,定义一个结构体Item,cur为当前到达的单词,用一个和wordList大小相同的字符串used表示走到cur时走过的单词,step表示当前走过的步数。很可惜,这个版本的代码提交MLE,超空间了。肯定是因为Item这个结构体太大了,两个string,一个int。

    后来想到一个优化的方法,当某条路径path1走到单词cur时,其他路径就没必要再经过cur了,因为一旦经过cur,必定后续步骤和path1后续要走的路径就相同了,造成了重复搜索。所以我们走过每个单词时,可以直接将该单词置空,这样后续就不会再走该路了,同时这样也不需要Item结构体了。新版代码如下:

    class Solution {
    public:
        bool differOne(const string& s1, const string& s2)
        {
            int cnt = 0;
            for (size_t i = 0; i < s1.size(); ++i) {
                if (s1[i] != s2[i]) {
                    ++cnt;
                    if (cnt > 1)
                        return false;
                }
            }
            return cnt == 1;
        }
        int ladderLength(string beginWord, string endWord, vector<string>& wordList)
        {
            size_t n = wordList.size();
            queue<string> q;
            q.push(beginWord);
            int step = 1;
            while (!q.empty()) {
                size_t len = q.size();
                for (size_t i = 0; i < len; ++i) {
                    string t = q.front();
                    q.pop();
                    if (t == endWord)
                        return step;
                    for (size_t j = 0; j < wordList.size(); ++j) {
                        if (wordList[j] != "" && differOne(t, wordList[j])) {
                            q.push(wordList[j]);
                            wordList[j] = "";
                        }
                    }
                }
                ++step;
            }
            return 0;
        }
    };

    本代码提交AC,用时632MS。

    二刷。设置一个visited数组也许是更常规的做法:

    class Solution {
    public:
    	bool IsConnect(const string &a, const string &b) {
    		int diff = 0, n = a.size();
    		for (int i = 0; i < n; ++i) {
    			if (a[i] != b[i]) {
    				++diff;
    				if (diff > 1)return false;
    			}
    		}
    		return diff == 1;
    	}
    	int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
    		int n = wordList.size();
    		if (n == 0)return 0;
    
    		vector<int> visited(n, 0);
    		queue<string> q;
    		q.push(beginWord);
    		int ans = 0;
    		while (!q.empty()) {
    			++ans;
    			int m = q.size();
    			for (int i = 0; i < m; ++i) {
    				string cur = q.front();
    				if (cur == endWord)return ans;
    				q.pop();
    				for (int j = 0; j < n; ++j) {
    					if (visited[j] == 0 && IsConnect(cur, wordList[j])) {
    						visited[j] = 1;
    						q.push(wordList[j]);
    					}
    				}
    			}
    		}
    		return 0;
    	}
    };

    本代码提交AC,用时532MS。

    LeetCode Jump Game II

    45. Jump Game II

    Given an array of non-negative integers, you are initially positioned at the first index of the array.

    Each element in the array represents your maximum jump length at that position.

    Your goal is to reach the last index in the minimum number of jumps.

    Example:

    Input: [2,3,1,1,4]
    Output: 2
    Explanation: The minimum number of jumps to reach the last index is 2.
        Jump 1 step from index 0 to 1, then 3 steps to the last index.

    Note:

    You can assume that you can always reach the last index.


    上一题是问能否跳到数组右边,本题问最少需要多少跳能跳到数组右边,第一反应是DP。
    设dp[i]表示从位置i跳到数组右边最少需要的跳数,则dp[n-1]=0,dp[0]就是最终结果。此时我们需要从数组右边往左边遍历,假设dp[i+1,…,n-1]都求出来了,现在要求dp[i]。站在位置i,我们最远能跳到i+A[i],所以我们就看看从i跳到哪个$j\in[i,i+A[i]]$能获得最少的跳数,从i跳到j需要增加一跳,所以递推式就是$dp[i]=min\{dp[j]+1\},\quad j\in[i+1,i+A[i]]$

    这种思路的代码如下:

    class Solution {
    public:
        int jump(vector<int>& nums)
        {
            int n = nums.size();
            if (n <= 1)
                return 0;
            int maxJump = n – 1;
            vector<int> dp(n, 0);
            for (int i = n – 2; i >= 0; –i) {
                int curMinJump = maxJump;
                for (int j = i + 1; j <= nums[i] + i && j < n; ++j) {
                    curMinJump = min(curMinJump, dp[j] + 1);
                }
                dp[i] = curMinJump;
            }
            return dp[0];
        }
    };

    可惜,本代码提交TLE,在最后一个大数据集上超时了。但是我还是觉得DP方法是一个基本法,而且如果要求出最小跳的路径,也很方便,如下代码,打印的时候,从pre[n-1]不断找前驱位置就好。

    class Solution {
    public:
        int jump(vector<int>& nums)
        {
            int n = nums.size();
            if (n <= 1)
                return 0;
            int maxJump = n – 1;
            vector<int> dp(n, 0), pre(n, 0);
            for (int i = n – 2; i >= 0; –i) {
                int curMinJump = maxJump, k = 0;
                for (int j = i + 1; j <= nums[i] + i && j < n; ++j) {
                    if (dp[j] + 1 < curMinJump) {
                        curMinJump = dp[j] + 1;
                        k = j;
                    }
                }
                dp[i] = curMinJump;
                pre[k] = i; // 从i跳到k能获得最小跳数
            }
            return dp[0];
        }
    };

    讨论区看到一个O(n)的解法,很厉害。思路和上一题LeetCode Jump Game很类似,依旧维护一个当前能跳到的最远位置curFarthest,同时维护一个当前跳能跳到的范围[curBegin, curEnd],一旦i超过了curEnd,说明应该开启下一跳了,同时更新curFarthest。

    这种算法的思路很巧妙,他不管你当前跳到底跳到哪里,只管你能够跳到的范围,一旦走出这个范围,就必须开启下一跳了。代码如下:

    class Solution {
    public:
        int jump(vector<int>& nums)
        {
            int jumps = 0, curEnd = 0, curFarthest = 0;
            for (int i = 0; i < nums.size() – 1; ++i) {
                curFarthest = max(curFarthest, i + nums[i]);
                if (i == curEnd) {
                    curEnd = curFarthest;
                    ++jumps;
                    if (curEnd >= nums.size() – 1)
                        break;
                }
            }
            return jumps;
        }
    };

    本代码提交AC,用时22MS。第10行是增加的提前结束的代码。
    参考:

    二刷。这题很有意思,时隔三年再刷此题,依然不会。首先想到的依然是DP的解法,不过这个DP比以前写的DP好理解一些,代码如下。思想就是steps[i]保存了要跳到i位置最小的步数,则初始时steps[0]=0,其他位置都是无穷大。然后,对于每个位置i,更新它能跳到的所有target位置的最小步数。最后返回steps[n-1]。

    class Solution {
    public:
    	int jump(vector<int>& nums) {
    		int n = nums.size();
    		vector<int> steps(n, INT_MAX);
    		steps[0] = 0;
    		for (int i = 0; i < n; ++i) {
    			for (int j = 1; j <= nums[i]; ++j) {
    				int target = i + j;
    				if (target < n) {
    					steps[target] = min(steps[target], steps[i] + 1);
    				}
    				else {
    					break;
    				}
    			}
    		}
    		return steps[n - 1];
    	}
    };

    同样,DP解法在最后一个样例上TLE了。

    评论区看到另一个O(n)的解法,比之前的更好理解: https://leetcode.com/problems/jump-game-ii/discuss/18028/O(n)-BFS-solution。这个解法把该问题想象为一个BFS层次遍历的问题。

    以输入样例s=[2,3,1,1,4]为例,最开始我们在起点s[0],它最多能跳2步,所以能跳到s[1]和s[2]的位置,即3,1在BFS的同一层。s[1]=3能跳到除了该层的s[3]和s[4],即1,4在BFS的下一层;s[2]=1无法跳出第二层。因为4在最后一层,也就是第3层,所以最少可以2步到达。

    2
    3,1
    1,4

    代码如下:

    class Solution {
    public:
    	int jump(vector<int>& nums) {
    		int level = 0, current_fastest = 0, next_fastest = 0;
    		int n = nums.size(), i = 0;
    		if (n == 1)return 0;
    		while (true) {
    			++level;
    			while (i <= current_fastest && i < n) {
    				next_fastest = max(next_fastest, i + nums[i]);
    				++i;
    			}
    			if (next_fastest >= n - 1)return level;
    			current_fastest = next_fastest;
    		}
    		return 0;
    	}
    };
    

    本代码提交AC,用时8MS。由于i指针一直在往前走,所以时间复杂度为O(n)。

    三刷。 依然是BFS的思路,代码简洁易懂:

    class Solution {
    public:
        int jump(vector<int>& nums) {
            int n = nums.size();
            int step = 0, pre_max = 0, next_max = 0;
            for(int i = 0; i < n; ++i) {
                if(i > pre_max) {
                    ++step;
                    pre_max = next_max;
                } 
                next_max = max(next_max, i + nums[i]);
            }
            return step;
        }
    };

    本代码提交AC,用时28MS。

    LeetCode Jump Game

    55. Jump Game

    Given an array of non-negative integers, you are initially positioned at the first index of the array.

    Each element in the array represents your maximum jump length at that position.

    Determine if you are able to reach the last index.

    Example 1:

    Input: [2,3,1,1,4]
    Output: true
    Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
    

    Example 2:

    Input: [3,2,1,0,4]
    Output: false
    Explanation: You will always arrive at index 3 no matter what. Its maximum
                 jump length is 0, which makes it impossible to reach the last index.
    

    给定一个数组,每个数字A[i]表示站在i位置最多能跳的步数,初始时站在数组左边,问能否跳到数组右边。
    使用贪心策略,维护一个当前能跳到的最远位置maxpos。从左往右遍历数组,如果位置i>maxpos,说明根据之前的数字,没办法跳到位置i,直接break;否则更新maxpos为max(maxpos,A[i]+i)。最后检查maxpos>=n-1。完整代码如下:

    class Solution {
    public:
        bool canJump(vector<int>& nums)
        {
            int maxpos = 0;
            for (int i = 0; i < nums.size(); ++i) {
                if (i > maxpos)
                    break;
                maxpos = max(maxpos, i + nums[i]);
            }
            return maxpos >= nums.size() – 1;
        }
    };

    本代码提交AC,用时12MS。

    二刷。是LeetCode Jump Game II的简化版,依然将问题转化为BFS,如果在BFS某一层时,最远距离没有更新,则表示没法再往前跳了。最后看看最远距离能否到达末尾即可。完整代码如下:

    class Solution {
    public:
    	bool canJump(vector<int>& nums) {
    		int n = nums.size(), i = 0;
    		int cur_fastest = 0, next_fastest = 0;
    		while (i < n) {
    			while (i < n&&i <= cur_fastest) {
    				next_fastest = max(next_fastest, i + nums[i]);
    				++i;
    			}
    			if (next_fastest >= n - 1)return true;
    			if (cur_fastest == next_fastest)return false;
    			cur_fastest = next_fastest;
    		}
    		return false;
    	}
    };

    本代码提交AC,用时4MS。

    三刷。在每个位置,我们可以算到该位置能跳到的最远位置maxindex,如果当前位置i在maxindex范围内,说明可以到达i,且能从i进行新的起跳,所以可更新maxindex。如果i超过maxindex,说明无法到达i,也就无法在i的基础上进行新的起跳了。最后看看maxindex能否到达末尾即可。

    完整代码如下:

    class Solution {
    public:
    	bool canJump(vector<int>& nums) {
    		int maxindex = 0, n = nums.size();
    		for (int i = 0; i < n; ++i) {
    			if (i > maxindex)break;
    			maxindex = max(maxindex, i + nums[i]);
    		}
    		return maxindex >= n - 1;
    	}
    };

    本代码提交AC,用时8MS。

    LeetCode Binary Tree Right Side View

    199. Binary Tree Right Side View

    Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.

    Example:

    Input: [1,2,3,null,5,null,4]
    Output: [1, 3, 4]
    Explanation:
    
       1            <---
     /   \
    2     3         <---
     \     \
      5     4       <---

    给定一棵二叉树,问从树的右边往左看,从上往下,看到的数组是什么。很有意思哈,一棵树变着花样出题。
    想到的话,其实很简单,从右边看到的数是每一层最右边的数(好像是废话),所以我们可以对树层次遍历,取出每层最右边的数,构成的数组就是答案。完整代码如下:

    class Solution {
    public:
        vector<int> rightSideView(TreeNode* root)
        {
            vector<int> ans;
            if (root == NULL)
                return ans;
            queue<TreeNode*> q;
            q.push(root);
            while (!q.empty()) {
                ans.push_back(q.back()->val);
                int n = q.size();
                for (int i = 0; i < n; ++i) {
                    TreeNode* tmp = q.front();
                    q.pop();
                    if (tmp->left)
                        q.push(tmp->left);
                    if (tmp->right)
                        q.push(tmp->right);
                }
            }
            return ans;
        }
    };

    本代码提交AC,用时3MS。