4专题总结-图论和DFS、BFS

1图论:

1.1  133. Clone Graph

https://leetcode.com/problems/clone-graph/#/description

思路:这题可以对照拷贝随机链表那道题,首先拷贝图中的节点,然后拷贝每个结点的neighbors数组。

这题使用BFS,所有的BFS都可以看成是queue和unordered_map的组合实现的,该题模拟实现了一个queue,主要是因为后面要判断某个元素是否已经访问,所以使用一个vector来模拟操作,传统的queue会弹出元素,还需要一个vector来辅助,比较麻烦。拷贝节点前要使用一个if(Map.find(cur -> neighbors[i]) == Map.end()),没找到才将元素复制到一个map里面,key是旧节点,value是新节点,记住因为是拷贝,所以新节点必须是new出来的节点,不然指针还是指向原来的元素,出错。后面拷贝neighbor数组的时候也要注意这点,必须将map中的value当成拷贝数组中的元素(这点很重要)。 Map[qNode[j]] -> neighbors.push_back(  Map[  qNode[j] -> neighbors[k] ]  );

map操作总结:

插入使用insert函数,查找每个元素是否在map里面,使用map.find(),如果没找到就对应的指针指向map.end();

使用下标操作找到对应的value,如果需要找的key不在map里面,就会自动将可以保存起来,默认初始化,比如map里面没有1,执行下标操作map[1]之后,map里面就会有 1.

insert插入和make_pair组合使用。

/**
 * Definition for undirected graph.
 * struct UndirectedGraphNode {
 *     int label;
 *     vector<UndirectedGraphNode *> neighbors;
 *     UndirectedGraphNode(int x) : label(x) {};
 * };
 */
class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if(node == NULL){
            return NULL;
        }
        vector<UndirectedGraphNode *> qNode;
        unordered_map<UndirectedGraphNode *,UndirectedGraphNode *> Map;
        qNode.push_back(node);
        Map.insert(make_pair(node,new UndirectedGraphNode(node -> label)));
        int start = 0;
        //clone nodes
        while(start < qNode.size()){
            UndirectedGraphNode *cur = qNode[start];
            for(int i= 0;i < cur -> neighbors.size();++i){
                if(Map.find(cur -> neighbors[i]) == Map.end()){
                    qNode.push_back(cur -> neighbors[i]);
                    Map.insert(make_pair(cur -> neighbors[i],
                                         new UndirectedGraphNode(cur -> neighbors[i] -> label))); 
                  
                }
               
            }
            ++start;
        }
        
       // clone neighbors
        for(int j = 0;j < qNode.size();++j){
            for(int k = 0;k < qNode[j] -> neighbors.size();++k){
               // UndirectedGraphNode *tmp = new UndirectedGraphNode(qNode[j] -> neighbors[k] -> label);
                Map[qNode[j]] -> neighbors.push_back(Map[qNode[j] -> neighbors[k]]);
            }
        }
        return Map[node];
        
    }
};
clone graph

 1.2 Topological Sorting

http://www.lintcode.com/en/problem/topological-sorting/

思路:三步走:

1)不断统计每个节点的入度,如果节点已经存在map里面,就执行增1操作,如果不在,就创建一个节点

2)找到入度为零的点,记得必须要从neighbor数组里面进行统计,这样才能得到链接关系。

3)不断的删除入度,将删除后入度为零的点加入到结果中,直到队列为空为止

/**
 * Definition for Directed graph.
 * struct DirectedGraphNode {
 *     int label;
 *     vector<DirectedGraphNode *> neighbors;
 *     DirectedGraphNode(int x) : label(x) {};
 * };
 */
class Solution {
public:
    /**
     * @param graph: A list of Directed graph node
     * @return: Any topological order for the given graph.
     */
    vector<DirectedGraphNode*> topSort(vector<DirectedGraphNode*> graph) {
        // write your code here
        //统计每个结点的入度
        vector<DirectedGraphNode*> result;
        unordered_map<DirectedGraphNode*,int> Map;
        for(DirectedGraphNode* node : graph){
            for(DirectedGraphNode* neigh : node -> neighbors)
            if(Map.find(neigh) != Map.end()){
                Map[neigh] = Map[neigh] + 1;
            }
            else{
                Map.insert(make_pair(neigh,1));
            }
        }
        //找入度为零的节点
        queue<DirectedGraphNode*> q;
        for(DirectedGraphNode* tmp : graph){
            if(Map[tmp] == 0){
                q.push(tmp);
                result.push_back(tmp);
            }
        }
        //不断删除入度
        while(!q.empty()){
            DirectedGraphNode* tmp = q.front();
            q.pop();
            for(DirectedGraphNode* neigh : tmp -> neighbors){
                Map[neigh] = Map[neigh] - 1;
                if(Map[neigh] == 0){
                    q.push(neigh);
                    result.push_back(neigh);
                }
            }
        }
    return result;
    }
};
topological sorting

2.DFS 解决搜索all的问题,只要求所有方案的,肯定是排列组合的搜索问题,搜索问题都是递归求解。

 Permutations排列问题的时间复杂度是n!(选第一个有n种情况,第二个有n-1种情况。。。。。)。需要将所有情况都走一遍的程序算法时间复杂度都是n!。

subsets复杂度分析:每个元素有选和不选两种选择,所以是2^n。

2.1  46. Permutations

https://leetcode.com/problems/permutations/#/description

思路:使用helper函数,这里使用一个整数n,递归的时候作为递归基,n = 0的时候,说明每个数都已经排列完了,就可以作为一次排列结果压入result中,for循环是关键,每次交换一次数,调用递归之后,需要将刚才调用的数交换回来。理解时候,可以先不管递归,先理一遍for循环,就可以知道第一个数放置的排列情况。

记住为什么这里是start + 1,而下面的subsets是i + 1?

答:permutations每次都是后面n - 1 个元素的全排列,比如[1,2,3],第一位是1的时候,求2,3;第一位是2的时候,求1,3;第一位是3的时候,求1,2.

subsets中每次都是固定前面i个元素,求后面i - 1个元素的子集,前面是1的时候,求2,3的子集,前面是1,2的时候,求3的子集,所以是i  + 1。

permutation一共运行sz次。

permutation从0开始到end,subsets从i + 1 ~end。

class Solution {
public:
    void helper(vector<vector<int>>& result,vector<int>& nums,int n){
        if(n == 0){
            result.push_back(nums);
        }
        for(int i =0;i <= n;++i){
            swap(nums[i],nums[n]);
            helper(result,nums,n - 1);
            swap(nums[n],nums[i]);
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> result;
        if(nums.size() == 0){
            return result;
        }
        helper(result,nums,nums.size() - 1);
        return result;
    }
};
permutations

 2.2 78. Subsets

https://leetcode.com/problems/subsets/#/description

思路:更正不需要先排序。

首先要对数组排序,每次将比前面数大的数字加入到tmp数组里面,然后递归,接下来pop_back还原(pop_back可以将vector最后一个元素删掉)

helper中记住一定是i+1,而不是start + 1;因为在同一个循环当中,start值是不变的,这样就会使得递归次数变多,所以必须是i+1;注意!!start + 1使得循环次数要多很多,所以必须要使用 i + 1;

每个元素可以选也可以不选,时间复杂度是O(2^n);

class Solution {
public: 

    void helper(vector<vector<int>>& result,vector<int>& nums,vector<int>& subset,int start){        
        result.push_back(subset);        
        for(int i = start;i < nums.size();++i){
            subset.push_back(nums[i]);
            helper(result,nums,subset,i + 1);
            subset.pop_back();
        }
    }
   
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        if(nums.size() == 0){
            return result;
        }
        vector<int> subset;
        sort(nums.begin(),nums.end());       
        helper(result,nums,subset,0);
        return result;
    }
};
subsets

 2.3 90. Subsets II

https://leetcode.com/problems/subsets-ii/#/description

s思路:1)该题需要一步一步的理清楚,helper(result,nums,tmp,i + 1);//这里记住是i+1,不要写成start+ 1;(因为在同一个循环当中,start值是不变的,这样就会使得递归次数变多,所以必须是i+1;)

2)

if(i != start && nums[i] == nums[i -1]){
  continue;
}

对于有重复元素的处理一定需要排序。

去重的方法,有重复元素就跳过,进行下一次循环。举例{1,2,2}的集合一个元素的情况。

class Solution {
public:
    void helper(vector<vector<int>>& result,vector<int>& nums,vector<int>& tmp,int start){
        result.push_back(tmp);
        for(int i = start;i < nums.size();++i){
            if(i != start && nums[i] == nums[i -1]){
                continue;
            }
            tmp.push_back(nums[i]);
            helper(result,nums,tmp,i + 1);//这里记住是i+1,不要写成start+ 1;
            cout << i + 1 << " ";
            tmp.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> result;
        if(nums.size() == 0){
            return result;
        }
        
        vector<int> tmp;
        int start = 0;
        sort(nums.begin(),nums.end());
        helper(result,nums,tmp,start);
        return result;
    }
};
subsets ii

 2.4 permutations II

https://leetcode.com/problems/permutations-ii/#/description

思路:综合permutation I和subsets II的去重方法,定义一个visit矩阵,

需要处理的情况是:我们先把Num排序,然后只能连续地选,这样就可以避免生成重复的solution.
例子:1 2 3 4 4 4 5 6 7 8
444这个的选法只能:4, 44, 444连续这三种选法

我们用一个visit的数组来记录哪些是选过的。

 if(i != 0 && nums[i] == nums[i - 1] && visit[i - 1] == 0 || visit[i] == 1){
                continue;
  }
i != 0才能保证后面判断 i - 1有效,nums[i] == nums[i - 1]前后两个元素相等才需要进行判断,visit[i - 1] == 0,第i - 1个元素没被访问,必须退出。前面属于一种情况,第二种情况就是
visit[i] == 1,这个时候该元素已经被访问了,就不需要计算,进入下一次循环。
class Solution {
public:
    void helper(vector<vector<int>>& result,vector<int>& nums,vector<int>& tmp,vector<int>& visit){
        
        if(tmp.size() == nums.size()){
            result.push_back(tmp);            
        }
        for(int i = 0;i <= nums.size() - 1;++i){
            if(i != 0 && nums[i] == nums[i - 1] && visit[i - 1] == 0 || visit[i] == 1){
                continue;
            }
            visit[i] = true;
            tmp.push_back(nums[i]);
            helper(result,nums,tmp,visit);
            tmp.pop_back();
            visit[i] = false;
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<int> visit(nums.size(),0);
        vector<vector<int>> result;
        vector<int> tmp;
        if(nums.size() == 0){
            return result;
        }
        sort(nums.begin(),nums.end());
        helper(result,nums,tmp,visit);
        return result;
    }
};
permutations II

这里深入理解下permutation I 和permutation II,只记第二个有重复元素的版本就可以了

排列合模板总结
使用范
几乎所有的搜索问题
根据具体目要求行改
什么
哪些情况需要跳

 2.5 N Queens N皇后问题

https://leetcode.com/problems/n-queens/#/description

思路:就是看成permutation那道题,求排列,把n个皇后看成n个数,每种排列就是一个皇后放置的方案,比如n = 4,那么vector<int> row{1,2,3,4}这种排列就代表皇后的放置方案中的一种可能结果,row[0] = 1,表示在第0行第1列放置一个皇后,row的大小代表行数。

1)何时将当前排列压入结果中??

使用一个判断函数isValid函数:这里需要注意为什么rows =  cols.size(),不需要减1操作,因为我们进行检查合法性操作的时候,肯定是要新建一行,就是判断下一行是否在column列rows行是否可以插入操作。所以不要rows减1;

bool isValid(vector<int>& cols,int column){//检查当前位置元素是否和已经放置的皇后冲突
        int rows = cols.size();
        for(int rowIndex = 0;rowIndex < rows;++rowIndex){
            if(cols[rowIndex] == column){//列冲突
                return false;
            }
            if(rowIndex + cols[rowIndex] == rows + column){//当前行已经放置皇后,检查下一行,所以rows不需要减1操作
                return false;//left上角 -> right下角对角线冲突
            }
            if(rowIndex - cols[rowIndex] == rows - column){//left下角 -> right上角对角线冲突
                return false;
            }
            
        }
        return true;
    }

然后再写一个转化结果的字符串函数,使用字符串的append操作。

cols[column]等于放置皇后的那一列,看哪一列等于皇后所在列就置为Q,其他的都是空格。
vector<string> toStr(vector<int>& cols){
        vector<string> resultRow;
        for(int column = 0;column < cols.size();++column){
            string s;
            for(int j = 0;j < cols.size();++j){                
                j == cols[column] ? s.append("Q") : s.append(".");
            }
            resultRow.push_back(s);
        }
        return resultRow;
    }
class Solution {
public:
    vector<string> toStr(vector<int>& cols){
        vector<string> resultRow;
        for(int column = 0;column < cols.size();++column){
            string s;
            for(int j = 0;j < cols.size();++j){                
                j == cols[column] ? s.append("Q") : s.append(".");
            }
            resultRow.push_back(s);
        }
        return resultRow;
    }
    bool isValid(vector<int>& cols,int column){//检查当前位置元素是否和已经放置的皇后冲突
        int rows = cols.size();
        for(int rowIndex = 0;rowIndex < rows;++rowIndex){
            if(cols[rowIndex] == column){//列冲突
                return false;
            }
            if(rowIndex + cols[rowIndex] == rows + column){//当前行已经放置皇后,检查下一行,所以rows不需要减1操作
                return false;//left上角 -> right下角对角线冲突
            }
            if(rowIndex - cols[rowIndex] == rows - column){//left下角 -> right上角对角线冲突
                return false;
            }
            
        }
        return true;
    }
    void helper(vector<vector<string>>& result,vector<int>& cols,int n){
        if(n == cols.size()){
            result.push_back(toStr(cols));
        }
        for(int column = 0;column < n;++column){
            if(!isValid(cols,column)){
                continue;
            }
            //接下来的push_back是对下一行进行操作放置皇后,新增加一行,新增加一个皇后才需要进行合法性判断
            cols.push_back(column);
            helper(result,cols,n);
            cols.pop_back();
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> result;
        if(n == 0){
            return result;
        }
        vector<int> cols;   
        helper(result,cols,n);
        return result;        
    }
};
N Queens
if(judge(tmp,tmp.size(),vec[i]) == false){
//这里传tmp.size()而不是i,因为要保证是全排列,如果从i开始就得不到全排列,因为每次i都是从i = 0开始,
所以,这次i = 3不符合条件,下次会找到3的防止位置
continue; }

2.6 Palindrome Partitioning

https://leetcode.com/problems/palindrome-partitioning/#/description

将一个字符串拆分为回文子串,求出所有的可能结果。

思路:类比permutation思路,主要两个地方1)string subStr = s.substr(start,i + 1 - start);//start开始的i + 1 - start个元素

2)

if(start == s.size()){//如果是s.size() - 1那么最后一个元素就会丢掉
result.push_back(tmp);
return;
}

这又是一道需要用DFS来解的题目,既然题目要求找到所有可能拆分成回文数的情况,那么肯定是所有的情况都要遍历到,对于每一个子字符串都要分别判断一次是不是回文数,那么肯定有一个判断回文数的子函数,还需要一个DFS函数用来递归,再加上原本的这个函数,总共需要三个函数来求解。代码如下:

class Solution {
public:
    bool isPalindrome(string s){
        int i = 0,j = s.size() - 1;
        for(i,j;i < j;++i,--j){
            if(s[i] != s[j]){
                return false;
            }
        }
        return true;
    }
    void helper(vector<vector<string>>& result,string s,vector<string>& tmp,int start){
        if(start == s.size()){//如果是s.size() - 1那么最后一个元素就会丢掉
            result.push_back(tmp);
            return;
        }
        for(int i = start;i <= s.size() - 1;++i){
            string subStr = s.substr(start,i + 1 - start);//start开始的i + 1 - start个元素
            if(!isPalindrome(subStr)){
                continue;
            }
            tmp.push_back(subStr);
            helper(result,s,tmp,i + 1);
            tmp.pop_back();
        }
    }
    vector<vector<string>> partition(string s) {
        vector<vector<string>> result;
        if(s.size() == 0){
            return result;
        }        
        vector<string> tmp;
        helper(result,s,tmp,0);
        return result;
    }
};
131. Palindrome Partitioning

2.7 39. Combination Sum

https://leetcode.com/problems/combination-sum/#/description

数组中没有重复的元素,数组中元素可以使用多次,找到所有相加等于目标元素的集合。

思路:这题没说给定的数组是否有序,所以需要先对数组排序。如果数组中有重复元素,每个答案中不能有重复元素,那么就需要数组去重的方法,就是使用两个i,j,类比双指针套路;

   第一点是记住每次递归的时候需要target - candidates[i],压入结果的条件是target减到0,,

   第二个很重要的点就是

if(target < candidates[i]){//这句很关键,不写的话没法target会小于0没法==0,超时。
                break;
            }这个是退出条件
第三个点就是helper(result,candidates,tmp,target - candidates[i],i);里面的i作为下一次的start,这里一定要想通,每次找候选数字,找到之后前面的数字肯定是没用的了,而该题每个答案是允许1,1,11,
这样的重复元素出现的,就是说每个数字可以使用多次,所以不需要i + 1,而是start = i;
for(int i = start;i < candidates.size();++i){
            if(target < candidates[i]){//这句很关键,不写的话没法target会小于0没法==0,超时。
                break;
            }
            tmp.push_back(candidates[i]);
            helper(result,candidates,tmp,target - candidates[i],i);
            tmp.pop_back();
        }
class Solution {
public:
    void helper(vector<vector<int> >& result,vector<int>& candidates,vector<int>& tmp,int target,int start){
        if(target == 0){
            result.push_back(tmp);
            return;
        }
        for(int i = start;i < candidates.size();++i){
            if(target < candidates[i]){//这句很关键,不写的话没法target会小于0没法==0,超时。
                break;
            }
            tmp.push_back(candidates[i]);
            helper(result,candidates,tmp,target - candidates[i],i);
            tmp.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int> > result;
        if(candidates.size() == 0){
            return result;
        }
        vector<int> tmp;  
        sort(candidates.begin(),candidates.end());//没说明数组是否有序,所以需要排序,如果说重复元素,还需要将数组去重
        helper(result,candidates,tmp,target,0);
        return result;
    }
};
conbination sum

2.8 40. Combination Sum II

https://leetcode.com/problems/combination-sum-ii/#/description

每个元素只能使用一次,找到所有组合等于target的元素集合。

思路:这题在上面的题目的基础上,结合了permutation II的重复元素处理方法,这题是给定的数组里面就有重复元素,但是数组中每个元素只能使用一次,使用一次的题目递归的start就必须每次i + 1,  helper(result,candidates,tmp,target - candidates[i],i + 1,visited);

这题做个总结:给定的数组有重复元素,每次是对剩下的元素进行运算,那么就应该考虑当前元素和之前一个元素是否相当,相等就跳过。

permutation II是每次都要从头开始考虑整个数组的全排列,所以需要一个visited数组,判断当前元素是否已经排列过,subsets II是剩下的数组元素进行考虑每次 start = i + 1,所以不需要维护一个visited数组,这题也是对剩下元素进行操作,所以和subsets II很像,当然permutation II是都可以的方法。permutation II的if判断方法包含所有的情况,是万能方法。

 if( candidates[i - 1] == candidates[i] && i != start){
                continue;//permutation II有重复元素的排列
            }
class Solution {
public:
    
    void helper(vector<vector<int>>& result,
                vector<int>& candidates,
                vector<int>& tmp,
                int target,int start,
                vector<int>& visited){
        if(target == 0){
            result.push_back(tmp);
        }
        for(int i = start;i < candidates.size();++i){
            if(target < candidates[i]){
                break;
            }
            if(visited[i] == 1 || visited[i] == 0 && candidates[i - 1] == candidates[i] && i != start){
                continue;//permutation II有重复元素的排列
            }
            visited[i] = 1;
            tmp.push_back(candidates[i]);
            helper(result,candidates,tmp,target - candidates[i],i + 1,visited);
            tmp.pop_back();
            visited[i] = 0;
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        if(candidates.size() == 0){
            return result;
        }
        vector<int> tmp,reDupCan;
        vector<int> visited(candidates.size(),0);
        int start = 0;
        sort(candidates.begin(),candidates.end());       
        helper(result,candidates,tmp,target,start,visited);
        return result;
    }
};
combination sum II使用permutation II的判断方法
class Solution {
public:
    
    void helper(vector<vector<int>>& result,
                vector<int>& candidates,
                vector<int>& tmp,
                int target,int start
                ){
        if(target == 0){
            result.push_back(tmp);
        }
        for(int i = start;i < candidates.size();++i){
            if(target < candidates[i]){
                break;
            }
            if( candidates[i - 1] == candidates[i] && i != start){//每次是对剩下的那部分进行运算
                continue;//subsets II有重复元素的排列
            }            
            tmp.push_back(candidates[i]);
            helper(result,candidates,tmp,target - candidates[i],i + 1);
            tmp.pop_back();
            
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        if(candidates.size() == 0){
            return result;
        }
        vector<int> tmp,reDupCan;        
        int start = 0;
        sort(candidates.begin(),candidates.end());       
        helper(result,candidates,tmp,target,start);
        return result;
    }
};
使用subsets II 的if判断方法

 3、BFS宽度优先搜索

从起始状态A变化到目标状态B,最小需要多少步,使用BFS求解。

求最短是多少使用BFS;

求所有的最短是多少使用DFS。

广度优先搜索时间复杂度和答案个数有密切关系,O(X * N + m),其中X是答案个数,N是总的节点数,m是BFS中访问的节点数目。

3.1 127. Word Ladder

https://leetcode.com/problems/word-ladder/#/description3

有一个起始单词变为目标单词,要求中间变化使用指定集合的元素。求出路径的长度。

思路:类比二叉树的层次遍历,主程序是差不多的,要注意的第一点是length初始化为1,不存在转化路径的时候返回0,题目中已经给出了,第二点是对于每个初始字符,要找到词典中和他相差一个编辑距离的单词,然后看这些单词是否已经访问或者是endword,如果不是就压入queue和unordered_set。

class Solution {
public:
    vector<string> getString(string word,unordered_set<string>& wordListSet){//找到词典中对应Word一个距离的所有单词
        vector<string> result;
        for(char c = 'a';c <= 'z';++c){
            for(int i = 0;i < word.size();++i){
                string s = word;                
                if(c == s[i]){
                    continue;
                }
                s[i] = c;
                if(wordListSet.find(s) != wordListSet.end()){
                    result.push_back(s);
                }
            }
        }
        return result;
    }
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        if(wordList.size() == 0){
            return 0;
        }
        if(beginWord == endWord){
            return 1;//读题,没转化数的时候才返回0
        }
        // wordList.push_back(beginWord);
        // wordList.push_back(endWord);//必须利用list中的单词进行转化,start和end不一定在list中,如果end不在list中,肯定不能进行转化得到
        unordered_set<string> hash,wordListSet;
        queue<string> q;
        hash.insert(beginWord);
        q.push(beginWord);
        for(string s : wordList){
            wordListSet.insert(s);
        }
        int length = 1;//从1开始,
        while(!q.empty()){
            ++length;
            int size = q.size();
            for(int i = 0;i < size;++i){
                string tmp = q.front();
                q.pop();
                for(string s : getString(tmp,wordListSet)){
                    if(hash.count(s) != 0){
                        continue;
                    }
                    if(s == endWord){//从1开始,则上一层就是路径长度,这时候q不包含endWord(s)
                        return length;
                    }
                    hash.insert(s);
                    q.push(s);
                }
            }
        }
        return 0 ;
        
    }
};
Word Ladder

 3.2 126. Word Ladder II

https://leetcode.com/problems/word-ladder-ii/#/description

思路:这道题目太难,主要看思路。给出所有的路径。

使用BFS从end到start进行,如果按照正常的方法从start找end,然后根据这个来构造路径,代价会比较高,因为保存前驱结点容易,而保存后驱结点则比较困难。所以我们在广度优先搜索时反过来先从end找start,最后再根据生成的前驱结点映射从start往end构造路径,这样算法效率会有明显提高。

原文地址:https://www.cnblogs.com/dingxiaoqiang/p/7132073.html