图论算法手撕题合集

图论算法

Posted by Jiayang Hu on September 2, 2025

图论算法属于量大管饱,模板硬套

200. 岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例一

输入:

grid = [
  ['1','1','1','1','0'],
  ['1','1','0','1','0'],
  ['1','1','0','0','0'],
  ['0','0','0','0','0']
]

输出:1

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int m = grid.size(), n = grid[0].size();
        auto dfs = [&](this auto&& dfs, int i, int j){
            if(i < 0 || i >= m || j < 0 || j >= n || grid[i][j] != '1'){
                return;
            }
            grid[i][j] ='2';
            dfs(i-1, j);
            dfs(i+1, j);
            dfs(i, j-1);
            dfs(i, j+1);
        };
        int ans = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == '1'){
                    ans++;
                    dfs(i, j);
                }
            }
        }
        return ans;
    }
};

994. 腐烂的橘子

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

值 0 代表空单元格; 值 1 代表新鲜橘子; 值 2 代表腐烂的橘子。 每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。

返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        bool cnt_two = false, cnt_one = false;
        queue<pair<int, int>> q;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == 2){
                    q.push({i, j});
                    cnt_two = true;
                }
                if(grid[i][j] == 1){
                    cnt_one = true;
                }
            }
        }
        if(cnt_one == false){
            return 0;
        }
        int ans = -1;
        int dirs[4][2] = { {0, -1}, {0, 1}, {1, 0}, {-1, 0} };
        while(!q.empty()){
            int s = q.size();
            ans++;
            for(int i = 0; i < s; i++){
                pair<int, int> p = q.front();
                q.pop();
                for(int k = 0; k < 4; k++){
                    int x = p.first + dirs[k][0], y = p.second + dirs[k][1];
                    if(x < m && x >= 0 && y >= 0 && y < n && grid[x][y] == 1){
                        grid[x][y] = 2;
                        q.push({x, y});
                    }
                }
            }
        }
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == 1){
                    return -1;
                }
            }
        }
        return ans;
    }
};

这题要一次A出来难度不小,主要是有特殊情况要特判,非常抽象了

207. 课程表

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& nums) {
        int n = nums.size();
        unordered_map<int, vector<int>> umap;
        vector<int> pre(numCourses, 0);
        for(int i = 0; i < n; i++){
            umap[nums[i][1]].push_back(nums[i][0]);
            pre[nums[i][0]]++;
        }
        queue<int> q;
        for(int i = 0; i < numCourses; i++){
            if(pre[i] == 0) {
                q.push(i);
            }
        }
        while(!q.empty()){
            int p = q.front();
            q.pop();
            for(auto t : umap[p]){
                if(--pre[t] == 0){
                    q.push(t);
                }
            }
        }
        for(int i = 0; i < numCourses; i++){
            if(pre[i] != 0) {
                return false;
            }
        }  
        return true;  
    }
};

这个题要做两件事:

  1. 建图。这个建图是边(头到尾的方式)
  2. 记录零入度的节点到队列

433. 最小基因变化

BFS算法 设计方式就是设计一个哈希表和哈希集合,前者关联基因string和变化次数,后者则是记录bank,参考单词划分那道题,两重循环。

class Solution {
public:
    int minMutation(string startGene, string endGene, vector<string>& bank) {
        vector<char> c = {'A', 'C', 'T', 'G'};
        unordered_set<string> uset(bank.begin(), bank.end());
        if(uset.find(endGene) == uset.end()) return -1;
        unordered_map<string, int> umap;
        queue<string> q;
        q.push(startGene);
        umap[startGene] = 0;
        if(uset.find(startGene) != uset.end()) uset.erase(startGene);
        while(!q.empty()){
            string p = q.front(); q.pop();
            for(int i = 0; i < 8; i++){
                string s = p;
                for(auto t : c){
                    if(s[i] != t){
                        s[i] = t;
                        if(s == endGene) return umap[p] + 1;
                        if(uset.find(s) != uset.end() && umap.find(s) == umap.end()){
                            umap[s] = umap[p] + 1;
                            uset.erase(s);
                            q.push(s);
                        }
                    }
                }
            }
        }
        return -1;
    }
};