案例1 - 迷宮最短路線問題
struct node {
node(int x, int y)
: _x(x)
, _y(y)
{}
int _x;
int _y;
};
bool BFS(vector<vector<int>> graph, int startx, int starty, int destx, int desty) {
//迷宮大小
int row = graph.size();
int col = graph[0].size();
//存儲迷宮位置
queue<node> q;
//用於標記走過的位置的
vector<vector<bool>> book(row, vector<bool>(col, false));
q.push(node(startx, starty));
book[startx][starty] = true;
//代表四個方位
int nextPos[4][2] = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };
//標記是否可以走出去
bool flag = false;
while (!q.empty()) {
for (int i = 0; i < 4; ++i) {
//計算新位置
int newx = q.front()._x + nextPos[i][0];
int newy = q.front()._y + nextPos[i][1];
//越界,直接進行下一次循環
if (newx >= row || newx < 0 || newy >= col || newy < 0)
continue;
//如果新的位置沒有障礙,並且沒有走過,保存新的位置
if (graph[newx][newy] == 0 && book[newx][newy] == false) {
q.push(node(newx, newy));
book[newx][newy] = true;
}
//如果新的位置是目標位置,結束查找
if (newx == destx && newy == desty) {
flag = true;
break;
}
//否則新位置有障礙,或者新位置已經走過,繼續循環下一個位置
}
//當前位置的四個方向都進行了尋找,如果找到了結束,找到的路徑一定是最短路徑
//如果沒有找到以四個方向的新節點爲起點開始新的一輪尋找
if (flag == true)
break;
q.pop();
}
//最後只需要返回 flag 就可以知道是否走出迷宮
return flag;
}
案例2 - 員工重要性
class Solution {
public:
int getImportance(vector<Employee*> employees, int id) {
//將員工信息存儲在鍵值對中 <id, Employee*>,方便使用
unordered_map<int, Employee*> m;
for (int i = 0; i < employees.size(); ++i) {
m.insert(make_pair(employees[i]->id, employees[i]));
}
//用一個隊列,計算當前員工時將該員工的下屬(id)都添加到隊列中
queue<int> q;
q.push(id);
//返回值:當前員工和他下屬的重要度之和
int result = 0;
//隊列不爲空,說明依舊有下屬的重要度沒有計算完畢
while (!q.empty()) {
//加和當前員工的重要度
result += m[q.front()]->importance;
//將當前員工的下屬添加到隊列中
vector<int> v(m[q.front()]->subordinates); //下屬表
for (int i = 0; i < v.size(); ++i) {
q.push(m[v[i]]->id);
}
//添加完下屬員工後,從隊列中將當前員工刪除(已經計算完畢)
q.pop();
}
return result;
}
};
案例3 - N叉樹的層序遍歷
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
if (root == nullptr)
return vector<vector<int>>();
//先打印當前節點的值,然後將利用當前節點帶出它的所有孩子節點(下一層)
//將節點都添加到隊列中,就可以實現層序遍歷的效果
queue<Node*> q;
//還有一個問題是怎麼將每一層分別存儲在二維數組中??
//建立一個數組,計算隊列中元素個數 qsize ,也就是當前行的元素個數,將當前行元素都添加到數組中
//並且將所有孩子節點添加到隊列中,刪除隊列的前 qsize 個元素(上一行的元素)
vector<int> v;
vector<vector<int>> result;
q.push(root);
while (!q.empty()) {
int qsize = q.size();
while (qsize--) {
//q 中的元素就是當前行的所有元素,循環 q.size() 次
//將當前行的所有元素 插入到二維數組的一行中
//然後帶出自己的孩子,然後將其刪除
v.push_back(q.front()->val); //插入到當前行
for (auto& e : q.front()->children) {
q.push(e); //帶出所有孩子節點
}
//刪除
q.pop();
}
//循環結束後: v 存儲的是一行的結果,q 中將上一行的元素全部刪除,q 中存儲的是新的一行的所有節點
result.push_back(v);
v.clear(); //清空
}
return result;
}
};
案例4 - 腐爛的橘子
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
if (grid.empty())
return 0;
//廣度優先搜索
//第一步先將所有腐爛的橘子添加到隊列中
//然後開始擴散,擴散的時候將新鮮橘子入隊,直到沒有新鮮的橘子結束
//然後最終循環判斷是否還存在新鮮橘子
//如果存在返回-1,否則返回擴散的步數
int row = grid.size();
int col = grid[0].size();
queue<pair<int, int>> q;
//最初添加所有腐爛橘子
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
if (grid[i][j] == 2)
q.push(make_pair(i, j));
}
}
//step 記錄擴散的步數,從-1開始,因爲即使不需要擴散下面對step仍然進行了一次++操作
//但是如果並沒有橘子,下面將不會進行++操作,也就是說step最終爲-1的情況就是沒有橘子的情況
//沒有橘子的情況step應該爲0,只需要最終進行處理
int step = -1;
//四個方向
int nextPos[4][2] = {{0,1}, {0,-1}, {-1,0}, {1,0}};
while (!q.empty()) {
int qsize = q.size();
while (qsize--) {
for (int i = 0; i < 4; ++i) {
int nx = q.front().first + nextPos[i][0];
int ny = q.front().second + nextPos[i][1];
if (nx < 0 || nx >= row || ny < 0 || ny >= col)
continue;
//新鮮橘子,即將腐爛
if (grid[nx][ny] == 1) {
q.push(make_pair(nx, ny));
grid[nx][ny] = 2;
}
}
//當前位置的四個方向都被感染
q.pop();
}
//所有腐爛橘子都進行了一次感染,step++
step++;
//此時隊列中的橘子都是新腐爛的橘子,舊腐爛的都已刪除
}
//最後循環查找,有沒有感染不到的橘子,有返回 -1
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
if (grid[i][j] == 1)
return -1;
}
}
return step < 0 ? 0 : step;
}
};
案例5 - 單詞接龍
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//將字典存儲在 unordered_set 中,查詢效率高
unordered_set<string> wordDict(wordList.begin(), wordList.end());
//記錄已經訪問過的單詞
unordered_set<string> visits;
queue<string> q;
//初始化操作
q.push(beginWord);
visits.insert(beginWord);
//最終你返回結果
int result = 1;
while (!q.empty()) {
//所有滿足當前步驟的單詞總數
int qsize = q.size();
while (qsize--) {
string fWord = q.front();
q.pop();
for (int i = 0; i < fWord.size(); ++i) {
//對字符串的每一個字符進行替換操作,但是隻會替換一個字符
string newWord = fWord;
for (auto ch = 'a'; ch <= 'z'; ++ch) {
newWord[i] = ch;
//替換完畢後檢測 fWord 是否在字典中
//不在字典中,說明轉換有誤,continue,在字典中但是已經轉換過不能再次轉換continue
//在字典中並且沒有轉換過那麼進行相應操作
if (!wordDict.count(newWord) || visits.count(newWord))
continue;
//轉換成功,則在上一步轉換的基礎上+1
if (newWord == endWord)
return result + 1;
//還沒有轉化成功但是當前轉換的單詞可能是轉換過程的一部分
visits.insert(newWord);
q.push(newWord);
}
}
}
//進行了一次轉換,轉換一次後得到的字符串如果匹配到了字典中,就會將這個字符串添加到隊列中
//這就是一次的轉換得到的所有可能結果
//在利用這些結果進行下一次轉換
result += 1;
}
//轉換不成功
return 0;
}
};
案例6 - 最小基因變化
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
if (bank.empty())
return -1;
//將基因庫存儲在 unordered_set 中,查找效率高
unordered_set<string> mBank(bank.begin(), bank.end());
//存儲已經轉換過的字符串
//解釋: 第2次轉換和第3次轉換的路徑不同但是最終都轉換成爲了同一個字符串,那麼只需要處理一個就ok
unordered_set<string> visits;
//所有可能的字符
vector<char> ch = {'A', 'C', 'G', 'T'};
queue<string> q;
//初始化
q.push(start);
visits.insert(start);
int step = 0;
while (!q.empty()) {
int qsize = q.size();
while (qsize--) {
string curGene = q.front();
q.pop();
//利用當前基因序列找出所有可能的下一步(只改動一個字符,並且在庫中)
for (int i = 0; i < 8; ++i) {
string newGene = curGene;
for (int j = 0; j < 4; ++j) {
newGene[i] = ch[j];
//成功轉換
if (newGene == end && mBank.count(newGene))
return (step + 1);
//如果基因庫中存在 newGene 並且沒有處理過 --> 可以進行轉換,添加到隊列中
if (mBank.count(newGene) && !visits.count(newGene)) {
q.push(newGene);
visits.insert(newGene);
}
}
}
}
//一次 while 循環結束意味着,當前步驟的所有基因序列都處理完畢
//此時隊列中存儲的是下一個步驟可能到達的基因序列(通過上一次步驟轉換而來,而且庫中存在)
step++;
}
//轉換失敗
return -1;
}
};
案例7 - 打開轉盤鎖
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
//unordered_set 存儲查找效率高
unordered_set<string> deadEnds(deadends.begin(), deadends.end());
if (deadEnds.find("0000") != deadEnds.end())
return -1;
//存儲已經處理過的數字
unordered_set<string> visits;
queue<string> q;
int step = 0;
//初始化
q.push("0000");
visits.insert("0000");
while (!q.empty()) {
int qsize = q.size();
while (qsize--) {
string curNum = q.front();
q.pop();
if (curNum == target)
return step;
for (int i = 0; i < 4; ++i) {
string newNum1 = curNum;
string newNum2 = curNum;
//撥動一次可能有兩種情況, -- 或 ++
newNum1[i] = newNum1[i] == '0' ? '9' : newNum1[i] - 1;
//如果撥動過的數字不在死亡列表中並且沒有處理過,添加到隊列中
if (!deadEnds.count(newNum1) && !visits.count(newNum1)) {
q.push(newNum1);
visits.insert(newNum1);
}
newNum2[i] = newNum2[i] == '9' ? '0' : newNum2[i] + 1;
if (!deadEnds.count(newNum2) && !visits.count(newNum2)) {
q.push(newNum2);
visits.insert(newNum2);
}
}
}
step++;
}
return -1;
}
};