BFS问题-LeetCode 55、45、5297、127、433、434(BFS)
【LeetCode #55】跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
class Solution {
public:
bool canJump(vector& nums) {
int maxReach = nums[0];
for(int i = 0; i <= maxReach && i < nums.size(); i++) {
maxReach = max(maxReach, i+nums[i]);
}
if (maxReach < nums.size()-1) return false; // 最远的元素
else return true;
}
};
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
【LeetCode #45】跳跃游戏II
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
class Solution {
public:
int jump(vector& nums) {
int maxReach = 0;
int step = 0;
int tmp = 0; // 保存上次的maxReach
for(int i = 0; i <= maxReach && i < nums.size()-1; i++) {
maxReach = max(maxReach, i+nums[i]);
if (i == tmp) {
step++;
tmp = maxReach;
}
}
return step;
}
};
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-ii
【LeetCode #5297】跳跃游戏III
这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]。
请你判断自己是否能够跳到对应元素值为 0 的 任意 下标处。
注意,不管是什么情况下,你都无法跳到数组之外。
示例 1:
输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3
解题思路:使用递归方法并使用dp数组来保存中间变量
class Solution {
public:
bool canReach(vector& arr, int start) {
vector dp(arr.size(), false);
return dfs(dp, arr, start);
}
private:
bool dfs(vector& dp, vector& arr, int start) {
if (start < 0 || start >= arr.size()) return false;
if (dp[start] == false) {
dp[start] = true;
if (arr[start] == 0) return true;
return dfs(dp, arr, start-arr[start]) || dfs(dp, arr, start+arr[start]);
} else
return false;
}
};
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-iii
【LeetCode #127】单词接龙
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
解题思路:
使用BFS算法,从代码结构来看,与之前二叉树层次遍历十分相似,首先看第一版,无优化版本,就是正常的BFS算法,使用flags数组来标记是否访问过,但这里,有个问题,每次查询是否转化时,都会调用canChange,但是很明显,如果wordList元素非常多,这样一个个查询就会非常慢的。
因此,在第二个版本,换了一个思路,首先建立一个hashset,然后将需要查询的beginWord中的每个字母都进行变化,然后再hashset中查询变化后的word是否存在。这样就不用遍历整个数组了,大大提高的效率!
(第一版,代码性能差的DFS,普通,无优化,2000ms)
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector& wordList) {
queue que;
que.push(beginWord);
vector flags(wordList.size()+1);
int layer = 1;
while(!que.empty()) {
layer++;
int size = que.size();
while(size--) {
string cur = que.front();
que.pop();
for(int i = 0; i < wordList.size(); i++) {
if (flags[i]) continue;
if (canChange(cur, wordList[i])) {
if (wordList[i] == endWord) return layer;
que.push(wordList[i]);
flags[i] = true;
}
}
}
}
return 0;
}
private:
bool canChange(string a, string b) {
int len = a.length();
int diff = 0;
for(int i = 0; i < len; i++) {
if (a[i] != b[i]) diff++;
}
return diff == 1;
}
};
(第二版,hashset优化后,100ms,快了10倍)
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector& wordList) {
int step = 0;
queue que;
unordered_set hashset(wordList.begin(), wordList.end());
if (!hashset.count(endWord)) return 0;
que.push(beginWord);
int len = beginWord.length(); // 字典中len相同
while(!que.empty()) {
step++; // BFS, 遍历完一层step++
int size = que.size();
while(size--) {
string cur = que.front();
que.pop();
for(int i = 0; i < len; i++) {
char c = cur[i]; // 缓存,下面要改变
for(int j = ‘a‘; j <= ‘z‘; j++) {
if (j == c) continue; // 跳过本身
cur[i] = j;
if (cur == endWord) return step+1;
if (!hashset.count(cur)) continue;
hashset.erase(cur); // 访问完就删除
que.push(cur);
}
cur[i] = c; // 可能一个单词可以转换多个单词
}
}
}
return 0;
}
};
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-ladder
【LeetCode #433】最小基因变化
一条基因序列由一个带有8个字符的字符串表示,其中每个字符都属于 "A", "C", "G", "T"中的任意一个。
假设我们要调查一个基因序列的变化。一次基因变化意味着这个基因序列中的一个字符发生了变化。
例如,基因序列由"AACCGGTT" 变化至 "AACCGGTA" 即发生了一次基因变化。
与此同时,每一次基因变化的结果,都需要是一个合法的基因串,即该结果属于一个基因库。
现在给定3个参数 — start, end, bank,分别代表起始基因序列,目标基因序列及基因库,请找出能够使起始基因序列变化为目标基因序列所需的最少变化次数。如果无法实现目标变化,请返回 -1。
解题思路:
这里只给出优化后的方案,与上一题思路一致,只是有些细小不同。
class Solution {
public:
int minMutation(string start, string end, vector& bank) {
queue que;
unordered_set hashset(bank.begin(), bank.end());
if(!hashset.count(end)) return -1;
hashset.erase(start);
hashset.erase(end);
int len = 8; // 8个
string seq = "ACGT";
int step = 0;
que.push(start);
while(!que.empty()) {
step++;
int size = que.size();
while(size--) {
string cur = que.front();
que.pop();
for(int i = 0; i < 8; i++) {
char ch = cur[i];
for(auto j: seq) {
if (j == ch) continue;
cur[i] = j;
if (cur == end) return step; // 返回变化次数
if (!hashset.count(cur)) continue;
hashset.erase(cur);
que.push(cur);
}
cur[i] = ch; // 复原
}
}
}
return -1;
}
};
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-genetic-mutation
【LeetCode #434】字符串中的单词数
统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。
请注意,你可以假定字符串里不包括任何不可打印的字符。
示例:
输入: "Hello, my name is John"
输出: 5
class Solution {
public:
int countSegments(string s) {
int i = 0;
while(i < s.length() && s[i] == ‘ ‘) i++;
if (i == s.length()) return 0;
int res = 0;
while(i < s.length()) {
while(i < s.length() && s[i] != ‘ ‘) i++;
res++;
while(i < s.length() && s[i] == ‘ ‘) i++;
}
return res;
}
};