面试经典150题 -- 图的广度优先遍历 (总结)

总的链接

面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

 909 . 蛇梯棋

链接 : 

. - 力扣(LeetCode)

题意 : 

 直接bfs就好了 , 题意难以理解 : 

class Solution:
    def snakesAndLadders(self, board: List[List[int]]) -> int:
        n = len(board)

        def id2rc(idx: int) -> (int, int):
            r, c = (idx - 1) // n, (idx - 1) % n
            if r % 2 == 1:
                c = n - 1 - c
            return n - 1 - r, c
        
        vis = set()
        q = deque([(1, 0)])
        while q:
            idx, step = q.popleft()
            for i in range(1, 6 + 1):
                idx_nxt = idx + i
                if idx_nxt > n * n:   # 超出边界
                    break
                
                x_nxt, y_nxt = id2rc(idx_nxt)   # 得到下一步的行列
                if board[x_nxt][y_nxt] > 0:   # 存在蛇或梯子
                    idx_nxt = board[x_nxt][y_nxt]
                if idx_nxt == n * n:   # 到达终点
                    return step + 1
                if idx_nxt not in vis:
                    vis.add(idx_nxt)
                    q.append((idx_nxt, step + 1))   # 扩展新状态
        
        return -1

433 .最小基因变化

类似于树中的层序遍历 , 用bfs实现爆搜就行 ;

对于在bank中与当前字符串只差一个字母且之前没有遍历过的字符串 进行下一层的遍历 ;

详细请看代码 : 

class Solution {
public:
    int minMutation(string s , string e , vector<string>& bank) {
        // 层序遍历 (bfs)
        unordered_set<string> st(bank.begin(), bank.end());
        queue<string> que;
        que.push(s);
        unordered_set<string> tag ;
        tag.insert(s);
        int ans = 0;
        while (!que.empty()) {
            int sz = que.size();
            for (int i = 0; i < sz; ++i) {
                string tmp = que.front(); que.pop();
                if (tmp == e) {
                    return ans;
                }
                for (char c : "ACGT") {
                    for (int j = 0; j < tmp.size(); ++j) {
                        string nt = tmp;
                        nt[j] = c;
                        if (st.count(nt) && !tag.count(nt)) {
                            que.push(nt);
                            tag.insert(nt);
                        }
                    }
                }
            }
            ans ++;
        }
        return -1;
    }
};

127 . 单词接龙

详细题解请看 : 

. - 力扣(LeetCode)

这里采用双向bfs进行解决 : 

class Solution {
public:
    string s, e ;
    set<string> st;

    int ladderLength(string bg, string ed, vector<string>& wl) {
        s = bg ; 
        e = ed ;
        // 将所有 word 存入set ,, 如果目标单词不在 set 中 , 说明无解
        for(string w : wl) st.insert(w) ;
        if(!st.count(e)) return 0 ;
        // 进行双向 bfs , 寻找 ans ;
        int ans = bfs() ;
        return ans == -1 ? 0 : ans + 1 ;
    }

    int bfs(){
        queue<string> d1 , d2 ;
        map<string , int> m1 , m2 ;
        // d1 代表从起点 beginWord 开始搜索(正向)
        // d2 代表从结尾 endWord 开始搜索(反向
        
        /*
         * m1 和 m2 分别记录两个方向出现的单词是经过多少次转换而来
         * e.g. 
         * m1 = {"abc":1} 代表 abc 由 beginWord 替换 1 次字符而来
         * m2 = {"xyz":3} 代表 xyz 由 endWord 替换 3 次字符而来
         */
        
         d1.push(s) ;
         m1[s] = 0 ;
         d2.push(e) ;
         m2[e] = 0 ;

         /*
         * 只有两个队列都不空,才有必要继续往下搜索
         * 如果其中一个队列空了,说明从某个方向搜到底都搜不到该方向的目标节点
         * e.g. 
         * 例如,如果 d1 为空了,说明从 beginWord 搜索到底都搜索不到 endWord,反向搜索也没必要进行了
         */
        while(!d1.empty() && !d2.empty()){
            int t = -1 ;
            if(d1.size() <= d2.size()){
                t = update(d1,m1,m2) ;
            }else{
                t = update(d2,m2,m1) ;
            }
            if(t!=-1) return t ;
        }
        return -1 ;
    }
    // update 代表deque中取出一个单词进行扩展
    // cur : 当前方向的距离词典, other : 另一个方向上的距离词典
    int update(queue<string>& que,map<string,int>& cur,map<string,int>& other){
        while(!que.empty()){
            string s = que.front() ; que.pop() ;
            int n = s.size() ;
            // 枚举哪一个字符需要进行替换
            for(int i=0;i<n;i++){
                // 枚举将s[i]替换成那个小写字母
                for(int j=0;j<26;j++){
                    // 替换之后字符串
                    string str = s.substr(0,i) + (char)(j+'a') + s.substr(i+1) ;
                    if(st.find(str)!=st.end()){
                        // 如果该字符串被[当前方向]记录过,跳过即可
                        if(cur.find(str) != cur.end()) continue;
                        // 如果在[另一方向]上出现过, 说明找到了最短路
                        if(other.count(str)){
                            return cur[s] + 1 + other[str] ;
                        }else{
                            // 否则加入que队列
                            que.push(str) ;
                            cur[str] = cur[s] + 1 ;
                        }
                    } 
                }
            }
        }
        return -1 ;
    }

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值