前言
把力扣学习计划之图论基础写完了,萌生出挑出几道我觉得好的题目出来的想法。
在“图论基础”学习计划中搜索占主体。本篇博客也将围绕搜索展开。
搜索中抓住:方向 + 去重
知识点:(循循渐进)
- 邻接矩阵
- 在矩阵中的搜索,这里更偏向 bfs 搜索,但是和 dfs 差不多,都是向四周拓展
- 邻接链表和在图中的搜索
- 计数搜索、起点到终点的最小步数搜索
- 二分图
题目 点击蓝色的文字跳转链接
一、岛屿数量(好经典的一道题)
解法:bfs、dfs、并查集。
这道题的评论区有很多优质解法,不再赘述。
写完这道题后你将了解 邻接矩阵、直观的搜索。
二、太平洋大西洋水流问题
知识点:多源bfs ,不了解的读者可以参考我原来的写过的一篇博客
什么是 多源bfs ? 无非是普通bfs初始化的时候把符合某条件的点全部加进队列中。
三、省份数量
知识点:邻接链表、图的深度优先搜索
对于每一个点搜索所有与他相连的点、并一直搜到底。
写了这题后读者将对图的常规表示形式(邻接矩阵、邻接链表)有一个大致的了解。
还有一种表示形式是结构体数组,只存储边的信息。感兴趣的读者可以翻阅我之前的博客学习Kruskal算法。
四、水壶问题
知识点:抽象图的 bfs
不是在图中的遍历,但是有 bfs 的特征。
搜索的本质是什么?我的理解是每次将所有可能加入队列、像一滴墨水滴入水中不停扩散、直到到达终点。
这题的难点是如何用哈希表表示pair
。这里使用自定义哈希函数来存储pair
。用到的函数是decltype
,同样用到过该函数的有堆的自定义排序。
typedef pair<int, int> PII;
class Solution {
public:
bool canMeasureWater(int a, int b, int c) {
queue<PII> q; //a b
q.push({0, 0});
auto hash_function = [](const PII &o) {
return hash<int>()(o.first) ^ hash<int>()(o.second);
};
unordered_set<PII, decltype(hash_function)> seen(0, hash_function);
while(q.size())
{
auto [x, y] = q.front(); q.pop();
if(x == c || y == c || x + y == c) return true;
if(seen.count({x, y})) continue;
seen.emplace(x, y);
//全x 全y 空x 空y x->y y->x
q.push({a, y});
q.push({x, b});
q.push({0, y});
q.push({x, 0});
q.push({x - min(x, b - y), y + min(x, b - y)});
q.push({x + min(a - x, y), y - min(a - x, y)});
}
return false;
}
};
五、最小基因变化
知识点:起点到终点带中间限制的最小步数
模板如下:
unordered_set<set> Set(a.begin(), a.end()); //查询 a是题目给的限制表
unordered_map<string, int> m; //word step 去重+计数
queue<string> q; //bfs
while(q.size())
{
auto it = q.front(); q.pop();
int val = m[it];
if(it == end) return val; //到了终点
for(int i = 0; i < it.size(); ++ i)
{
string s = it; //这里依据题目决定写在哪个循环的里面
for(int j = 0; j < t; ++ j) //次数依题意 含义是向外拓展所有可能
{
s[i] = j;
if(Set.count(s) && !m.count(s)) //与限制表挂钩 + 去重 + 计数
{
m.insert({s, val + 1});
q.push(s);
}
}
}
}
这题的 AC 代码:
class Solution {
public:
//bfs 将每一步能到达的加到队列里面
char ch[4] = {'A', 'C', 'G', 'T'};
int minMutation(string start, string end, vector<string>& bank) {
unordered_set<string> bankSet(bank.begin(), bank.end());
if(!bankSet.count(end)) return -1;
unordered_map<string, int> m;
m.insert({start, 0});
queue<string> q;
q.push(start);
while(q.size())
{
string s = q.front(); q.pop();
int val = m[s];
if(s == end) return val;
for(int i = 0; i < 8; ++ i)
{
string ss = s;
for(int j = 0; j < 4; ++ j)
{
ss[i] = ch[j];
if(bankSet.count(ss) && !m.count(ss))
{
m[ss] = val + 1;
q.push(ss);
}
}
}
}
return -1;
}
};
还有两个类似的题目供读者自测:
六、可能的二分法
知识点:二分图。读者看了这篇博客后能直接 AC
小结
- 力扣的图论基础入门难度挺友好的🤣kuangbin 的图练习人快写麻了。
- 基础题集围绕的全是搜索。