0
点赞
收藏
分享

微信扫一扫

关于Git 的基本概念和使用方式

船长_Kevin 2024-05-26 阅读 27
图论bfsdfs

👂 ▶ 怀抱的温柔并不属于我(弹唱版) (163.com)

👂 ▶ Gotta Have You (163.com)

👂 ▶ 心许百年 (163.com)

目录

🚩岛屿数量

AC  DFS

AC  BFS

AC  并查集

解释

代码

🌼腐烂的橘子

AC  多源bfs

🎂课程表

前置知识

AC  BFS

🌼实现 Trie(前缀树)

解释

代码


🚩岛屿数量

200. 岛屿数量 - 力扣(LeetCode)

AC  DFS

时间 O(mn):每个点遍历 1 次;空间 O(mn):所有点均为陆地,递归深度 m*n

class Solution {
public:
    int ans; 

    int nex[4][2] = {{0,1},{1,0},{0,-1},{-1,0}}; // 4 个方向

    // 从 x, y 开始深度优先搜索
    void dfs(vector<vector<char>>& grid, int x, int y) {
        int m = grid.size(), n = grid[0].size();

        grid[x][y] = '0'; // 大水漫灌 / 感染法

        for (int i = 0; i < 4; ++i) { 
            // 新的坐标
            int xx = x + nex[i][0], yy = y + nex[i][1];
            // 下一位置满足要求才递归,类似递归出口的处理
            if (xx < m && yy < n && xx >= 0 && yy >= 0 && (grid[xx][yy] == '1') )
                dfs(grid, xx, yy);
        }
    }

    int numIslands(vector<vector<char>>& grid) {
        int m = grid.size(), n = grid[0].size();
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j)
                if (grid[i][j] == '1') {
                    ans++; // 岛屿数量 +1
                    dfs(grid, i, j);
                }
        return ans;
    }
};

AC  BFS

时间 O(mn),空间 O(min(m, n))

class Solution {
public:
    int ans = 0; // 岛屿数量

    int nex[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; // 4 个方向

    int numIslands(vector<vector<char>>& grid) {

        queue<pair<int, int>> q; // 横纵坐标

        int m = grid.size(), n = grid[0].size();

        // 对陆地上每个点 bfs
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j) 
                if (grid[i][j] == '1') { // 只对陆地 bfs

                    q.push({i, j}); // 入队
                    ans++;
                    grid[i][j] = '0'; // 大水漫灌 / 感染法

                    // 逐层扩展
                    while (!q.empty()) {
                        auto cur = q.front(); // 取队头
                        q.pop();
                        int x = cur.first, y = cur.second; // 取坐标
                        // 4 个方向扩展
                        for (int k = 0; k < 4; ++k) {
                            int xx = x + nex[k][0], yy = y + nex[k][1];
                            if (xx < 0 || yy < 0 || xx >= m || yy >= n)
                                continue; // 不是 break
                            if (grid[xx][yy] == '1') { 
                                q.push({xx, yy});
                                grid[xx][yy] = '0'; // 感染法
                            }
                        }
                    }
                }
        return ans;
    }
};

AC  并查集

解释

代码

class Solution {
public:

    int dad[90000];
    int count = 0; // 岛屿数量
    int nex[4][2] = {{1,0},{-1,0},{0,1},{0,-1}}; // 4 个方向

    // 找领头
    int Find(int x) { 
        if (dad[x] == x)
            return x;
        else
            return dad[x] = Find(dad[x]); // 路径压缩
    }

    // 合并两个团体
    void join(int x, int y) {
        int xx = Find(x), yy = Find(y);
        if (xx != yy) {
            dad[yy] = xx;
            count--; // 合并陆地后,岛屿数量 -1
        }
    }

    int numIslands(vector<vector<char>>& grid) {

        int m = grid.size(), n = grid[0].size();

        // dad.resize(m*n); // 声明大小

        // 初始化 dad[]
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j)
                if (grid[i][j] == '1') {
                    count++;
                    dad[n*i + j] = n*i + j; // 自己的祖先是自己
                }
        // 全图遍历
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j) 
                if (grid[i][j] == '1') {
                    grid[i][j] = '0'; // 防止多次合并同一块陆地

                    // 对 4 个方向进行合并
                    for (int k = 0; k < 4; ++k) {
                        // 新的坐标
                        int x = i + nex[k][0], y = j + nex[k][1];
                        if (x < 0 || y < 0 || x >= m || y >= n)
                            continue;
                        if (grid[x][y] == '1')
                            join(n*i + j, n*x + y); // 合并两点
                    }
                }
        return count;
    }
};

🌼腐烂的橘子

994. 腐烂的橘子 - 力扣(LeetCode)

AC  多源bfs

时空 O(mn)

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {

        int nex[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};

        queue<pair<int, int>> q; // 存放坐标的队列
        int fresh = 0; // 新鲜橘子数
        int m = grid.size(), n = grid[0].size();
        int min = 0;

        // 初始化:得到新鲜橘子数 && 腐烂橘子加入队列
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1)
                    fresh++;
                if (grid[i][j] == 2)
                    q.push({i, j});
            }

        // 多源 bfs
        while (!q.empty() && fresh) {

            min++; 

            // 当前所有腐烂橘子,同时向 4 个方向扩散
            for (int size = q.size(); size; --size) {
                auto node = q.front(); // 队头
                q.pop();
                int x = node.first, y = node.second;
                // 腐烂上下左右(4 个方向)的橘子
                for (int i = 0; i < 4; ++i) {
                    int xx = x + nex[i][0], yy = y + nex[i][1];
                    // 越界
                    if (xx < 0 || yy < 0 || xx >= m || yy >= n)
                        continue;
                    // 新鲜的才能继续被感染
                    if (grid[xx][yy] == 1) {
                        grid[xx][yy] = 2;
                        fresh--; // 新鲜橘子 -1
                        q.push({xx, yy});
                    }
                }
            }
        }

        return fresh ? -1 : min;
    }
};

🎂课程表

207. 课程表 - 力扣(LeetCode)

前置知识

AC  BFS

思路 

要素

坑 

时间 O(m + n):初始化 = 边数 m(先修课程数)  +   拓扑排序 = 节点数 n

空间 O(m + n):队列 n;邻接表 m + n,表头节点 n 个,边数 m

class Solution {
public:
// 这里的 prerequisites 先修课程,实际就是 图的点和边
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {

int count = numCourses; // 统计入度为 0 课程总数

// 初始化:邻接表 和 节点的入度
vector<vector<int>> v(numCourses);
vector<int> in(numCourses);
int n = prerequisites.size();
for (int i = 0; i < n; ++i) {
// y -> x,y 是 x 的先修课程
// 图:节点 y 指向 节点 x
int x = prerequisites[i][0], y = prerequisites[i][1];
v[y].push_back(x); // 邻接表加边
in[x]++; // 节点 x 入度 +1
}

// 入度为 0 的点加入队列 q
priority_queue<int, vector<int>, greater<int>> q; // 最小堆
for (int i = 0; i < numCourses; ++i)
if (in[i] == 0) {
q.push(i);
count--; // 统计是否有环
}

// 拓扑排序, bfs 逐层扩展
while (!q.empty()) {
int node = q.top(); // 取入度为 0 的最小节点 node
q.pop();
// 根据邻接表 v[] 在 in[] 删除该点所有出边
for (int i = 0; i < v[node].size(); ++i) {
int k = v[node][i]; // node -> k,k 的入度 -1
in[k]--;
if (in[k] == 0) { // 入度为 0
q.push(k);
count--;
}
}
}
return count ? false : true;
}
};

🌼实现 Trie(前缀树)

208. 实现 Trie (前缀树) - 力扣(LeetCode)

解释

代码

m 个字母,插入,查询,前缀匹配,时间复杂度 都是 O(m)

字典树高度 n,最坏情况,不存在前缀相同的单词,空间复杂度 O(m^n) 

class Trie {
private:
bool isEnd; // 结束标志位
vector<Trie*> children; // 下一前缀树节点
public:
Trie() : isEnd(false), children(26) {}

void insert(string word) {
Trie* node = this; // 相当于根节点开始
// 遍历单词,类似构造链表,没有该字母,就新建节点
for (auto x : word) {
int ch = x - 'a'; // 字母偏移量
if (node->children[ch] == nullptr)
node->children[ch] = new Trie();
node = node->children[ch]; // 移动下一节点
}
node->isEnd = true;
}

bool search(string word) {
Trie* node = this; // 相当于根节点开始
// 遍历单词
for (auto x : word) {
int ch = x - 'a';
if (node->children[ch] == nullptr)
return false;
node = node->children[ch]; // 移动到下一字母
}
return node->isEnd; // 单词是否结束
}

bool startsWith(string prefix) {
Trie* node = this; // 根节点
// 遍历前缀
for (auto x : prefix) {
int ch = x - 'a';
if (node->children[ch] == nullptr)
return false;
node = node->children[ch]; // 移动到下一字母
}
return true; // 前缀存在
}
};

/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
举报

相关推荐

0 条评论