总的链接
面试经典 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 ;
}
};