面试经典150题 -- 图 (总结)

总的链接 : 

面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

 200 . 岛屿数量

链接 : 

. - 力扣(LeetCode)

总的思路 : 

遍历 ,每次遍历到1的时候且之前没有访问过的结点 (这个用一个标记数组tag实现) 采用dfs / bfs对其周围的所有为1的结点tag[i][j]全部标记为true ;

BFS

class Solution {
public:
    int dx[4] = {-1 , 0 , 1 , 0} ;
    int dy[4] = {0 , -1 , 0 , 1} ;
    int n , m ;

    void bfs(int x , int y , vector<vector<char>>& grid , vector<vector<bool>>& tag){
        queue<pair<int,int>> que ;
        que.push({x,y}) ;
        tag[x][y] = true ;
        while(!que.empty()){
            auto tmp = que.front() ;
            que.pop() ;
            int tx = tmp.first , ty = tmp.second ;
            for(int i=0;i<4;i++){
                int ntx = tx + dx[i] , nty = ty + dy[i] ;
                if(ntx>=0 && ntx <n && nty>=0 && nty <m){
                    if(!tag[ntx][nty] && grid[ntx][nty] == '1'){
                        que.push({ntx,nty});
                        tag[ntx][nty] = true ;
                    }
                } 
            }
        }
    }

    int numIslands(vector<vector<char>>& grid) {
        n = grid.size() ;
        m = grid[0].size() ;
        int ans = 0 ;
        vector<vector<bool>> tag = vector<vector<bool>>(n, vector<bool>(m, false));// 状态标记数组
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!tag[i][j] && grid[i][j] == '1'){
                    ans ++ ;
                    bfs(i , j , grid , tag) ;
                }
            }
        }
        return ans ;
    }
};

DFS

class Solution {
public:
    int dx[4] = {-1 , 0 , 1 , 0} ;
    int dy[4] = {0 , -1 , 0 , 1} ;
    int n , m ;

    void dfs(int x , int y , vector<vector<char>>& grid , vector<vector<bool>>& tag){
        for(int i=0;i<4;i++){
            int ntx = x + dx[i] , nty = y + dy[i] ;
            if(ntx>=0 && ntx <n && nty>=0 && nty <m){
                if(!tag[ntx][nty] && grid[ntx][nty] == '1'){
                    tag[ntx][nty] = true ;
                    dfs(ntx,nty,grid,tag);
                }
            } 
        }
    }

    int numIslands(vector<vector<char>>& grid) {
        n = grid.size() ;
        m = grid[0].size() ;
        int ans = 0 ;
        vector<vector<bool>> tag = vector<vector<bool>>(n, vector<bool>(m, false));// 状态标记数组
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!tag[i][j] && grid[i][j] == '1'){
                    ans ++ ;
                    dfs(i , j , grid , tag) ;
                }
            }
        }
        return ans ;
    }
};

130 . 被围绕的区域 : 

链接 : 

. - 力扣(LeetCode)

思路 : 

从地图周边出发,将周边空格相邻的'O'都做上标记,然后在遍历一遍地图,遇到 'O' 且没做过标记的,那么都是地图中间的'O',全部改成'X'就行。

这里有个小技巧 : 可以将遍历到的'0'位置改为其它特殊字符,比如A ;

class Solution {
private:
    int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1}; // 保存四个方向
    void dfs(vector<vector<char>>& board, int x, int y) {
        board[x][y] = 'A';
        for (int i = 0; i < 4; i++) { // 向四个方向遍历
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            // 超过边界
            if (nextx < 0 || nextx >= board.size() || nexty < 0 || nexty >= board[0].size()) continue;
            // 不符合条件,不继续遍历
            if (board[nextx][nexty] == 'X' || board[nextx][nexty] == 'A') continue;
            dfs (board, nextx, nexty);
        }
        return;
    }

public:
    void solve(vector<vector<char>>& board) {
        int n = board.size(), m = board[0].size(); 
        // 步骤一:
        // 从左侧边,和右侧边 向中间遍历
        for (int i = 0; i < n; i++) {
            if (board[i][0] == 'O') dfs(board, i, 0);
            if (board[i][m - 1] == 'O') dfs(board, i, m - 1);
        }

        // 从上边和下边 向中间遍历
        for (int j = 0; j < m; j++) {
            if (board[0][j] == 'O') dfs(board, 0, j);
            if (board[n - 1][j] == 'O') dfs(board, n - 1, j);
        }
        // 步骤二:
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (board[i][j] == 'O') board[i][j] = 'X';
                if (board[i][j] == 'A') board[i][j] = 'O';
            }
        }
    }
};

133 . 克隆图

. - 力扣(LeetCode)

就是实现深拷贝 , 用递归实现 : 

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;
    Node() {
        val = 0;
        neighbors = vector<Node*>();
    }
    Node(int _val) {
        val = _val;
        neighbors = vector<Node*>();
    }
    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/

class Solution {
public:
    unordered_map<Node*,Node*> mp ;
    Node* cloneGraph(Node* node) {
        if(node==nullptr) return node ;
        // 如果该结点已经访问过了,则直接从哈希表中取出对应的克隆结点返回
        if(mp.find(node)!=mp.end()){
            return mp[node] ;
        }

        // 克隆结点
        Node* clnode = new Node(node->val) ;
        mp[node] = clnode ;
        for(auto& neighbor : node->neighbors){
            clnode->neighbors.push_back(cloneGraph(neighbor));
        }
        return clnode ;
    }
};

399 . 除法求值 : 

链接 : 

. - 力扣(LeetCode)

带权并查集 : 

详细leetcode题解地址 : 

. - 力扣(LeetCode)

代码 : 

class Solution {
public:
    int p[30];
    double w[30] ;
    int find(int x){
        if(p[x]!=x){
            int fa = find(p[x]) ;
            w[x] = w[x] * w[p[x]] ;
            p[x] = fa ;
        }
        return p[x] ;
    }

    void merge(int x,int y,double val){// 合并集合
        int fx = find(x) , fy = find(y) ;
        p[fx] = fy ;
        w[fx] = val * 1.0 * w[y] / w[x] ;   
    }

    // 构建有向图 , e 和 v 可以表示成一个图
    // e中出现的变量就是图的顶点 , 分子和分母 可以表示成一个有向关系(形成一个带权有向图)

    vector<double> calcEquation(vector<vector<string>>& e, vector<double>& v, vector<vector<string>>& qt) {
        // e v q
        // 可以将e中两个变量所在的集合进行[合并]
        // 同在一个集合中的两个变量就可以通过某种方式计算出它们的比值 ;
        // 具体的说可以将不同的变量的比值转换成相同变量的比值,

        // 如果两个变量没有在同一个集合中 / 一个变量没有出现在任何集合中,直接返回-1.0

        // 利用并查集 , 对于每一个结点x来说,除了维护父节点p[x]之外,还要维护其权值w,
        // 其中[权值]w 指 : 结点 x 与 父亲p[x] 的 比值w[x] = v[x] / v[p[x]]

        // 当查询结点 x 父亲时 ,如果 p[x]!=x , 先找到p[x]的祖宗f,将p[x]更新为f ;
        // 也就是 将w[x] 跟新为 w[x] * w[f[x]] ;
        // w[x] = v[x]/v[f] = v[x]/v[p[x]]*v[p[x]]/v[f] = w[x]*w[p[x]] ;

        // 合并两个结点x,y时,我们需要先找到两者的父亲fx,fy,将p[fx]跟新为fy;
        // v[x]/v[y]=k , 则 w[fx] = k * w[y] / w[x]  
        int n = e.size()  ;
        unordered_map<string , int> mp ;
        int s = 0 ; // 种类数量
        for(int i=0;i<n;i++){
            if(mp.find(e[i][0]) == mp.end()){
                mp[e[i][0]] = s++ ;
            }
            if(mp.find(e[i][1]) == mp.end()){
                mp[e[i][1]] = s++ ;
            }
        }
        for(int i=0;i<s;i++) p[i] = i , w[i] = 1.0 ;
        for(int i=0;i<n;i++){
            int va = mp[e[i][0]] , vb = mp[e[i][1]] ;
            // 合并
            merge(va,vb,v[i]);
        }
        vector<double> ans ;
        for(const auto& q : qt){
            double res = -1.0 ;
            if(mp.find(q[0])!=mp.end() && mp.find(q[1])!=mp.end()){
                int na = mp[q[0]] , nb = mp[q[1]] ;
                int fa = find(na) , fb = find(nb) ;
                if(fa == fb){
                    res = w[na] / w[nb] ;
                }
                // else{
                //     res = -1.0 ;
                // }
            }
            ans.push_back(res) ;
        }
        return ans ;
    }
};

暴力BFS : 

这个就是暴力 , 找到每一种可能就行了 ;

class Solution {
public:
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
        int nvars = 0;
        unordered_map<string, int> variables;

        int n = equations.size();
        for (int i = 0; i < n; i++) {
            if (variables.find(equations[i][0]) == variables.end()) {
                variables[equations[i][0]] = nvars++;
            }
            if (variables.find(equations[i][1]) == variables.end()) {
                variables[equations[i][1]] = nvars++;
            }
        }

        // 对于每个点,存储其直接连接到的所有点及对应的权值
        vector<vector<pair<int, double>>> edges(nvars);
        for (int i = 0; i < n; i++) {
            int va = variables[equations[i][0]], vb = variables[equations[i][1]];
            edges[va].push_back(make_pair(vb, values[i]));
            edges[vb].push_back(make_pair(va, 1.0 / values[i]));
        }

        vector<double> ret;
        for (const auto& q: queries) {
            double result = -1.0;
            if (variables.find(q[0]) != variables.end() && variables.find(q[1]) != variables.end()) {
                int ia = variables[q[0]], ib = variables[q[1]];
                if (ia == ib) {
                    result = 1.0;
                } else {
                    queue<int> points;
                    points.push(ia);
                    vector<double> ratios(nvars, -1.0);
                    ratios[ia] = 1.0;

                    while (!points.empty() && ratios[ib] < 0) {
                        int x = points.front();
                        points.pop();

                        for (const auto [y, val]: edges[x]) {
                            if (ratios[y] < 0) {
                                ratios[y] = ratios[x] * val;
                                points.push(y);
                            }
                        }
                    }
                    result = ratios[ib];
                }
            }
            ret.push_back(result);
        }
        return ret;
    }
};

207 . 课程表

. - 力扣(LeetCode)

拓扑序模板题 , 我们只需要验证是否存在拓扑序即可 ;

使用dfs / bfs都可以 ;

class Solution {
private:
    // 存储有向图
    vector<vector<int>> edges;
    // 标记每个节点的状态:0=未搜索,1=搜索中,2=已完成
    vector<int> visited;
    // 判断有向图中是否有环
    bool valid = true;

public:
    void dfs(int u) {
        visited[u] = 1;
        for (int v: edges[u]) {
            if (visited[v] == 0) {
                dfs(v);
                if (!valid) {
                    return;
                }
            }
            else if (visited[v] == 1) {
                valid = false;
                return;
            }
        }
        visited[u] = 2;
    }

    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        edges.resize(numCourses);
        visited.resize(numCourses);
        for (const auto& info: prerequisites) {
            edges[info[1]].push_back(info[0]);
        }
        for (int i = 0; i < numCourses && valid; ++i) {
            if (!visited[i]) {
                dfs(i);
            }
        }
        return valid;
    }
};

210 . 课程表II

链接 : 

. - 力扣(LeetCode)

和上题一样 : 

class Solution {
private:
    // 存储有向图
    vector<vector<int>> edges;
    // 标记每个节点的状态:0=未搜索,1=搜索中,2=已完成
    vector<int> visited;
    // 用数组来模拟栈,下标 0 为栈底,n-1 为栈顶
    vector<int> result;
    // 判断有向图中是否有环
    bool valid = true;

public:
    void dfs(int u) {
        // 将节点标记为「搜索中」
        visited[u] = 1;
        // 搜索其相邻节点
        // 只要发现有环,立刻停止搜索
        for (int v: edges[u]) {
            // 如果「未搜索」那么搜索相邻节点
            if (visited[v] == 0) {
                dfs(v);
                if (!valid) {
                    return;
                }
            }
            // 如果「搜索中」说明找到了环
            else if (visited[v] == 1) {
                valid = false;
                return;
            }
        }
        // 将节点标记为「已完成」
        visited[u] = 2;
        // 将节点入栈
        result.push_back(u);
    }

    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        edges.resize(numCourses);
        visited.resize(numCourses);
        for (const auto& info: prerequisites) {
            edges[info[1]].push_back(info[0]);
        }
        // 每次挑选一个「未搜索」的节点,开始进行深度优先搜索
        for (int i = 0; i < numCourses && valid; ++i) {
            if (!visited[i]) {
                dfs(i);
            }
        }
        if (!valid) {
            return {};
        }
        // 如果没有环,那么就有拓扑排序
        // 注意下标 0 为栈底,因此需要将数组反序输出
        reverse(result.begin(), result.end());
        return result;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值