图 | 3道LeetCode常见笔试、面试题汇总

本文整理了3道图的LeetCode基础高频题,分别是「星型图中心节点」「岛屿的周长」「图像渲染」,涵盖图的基础判断、二维数组遍历、DFS/BFS核心用法,每道题提供多种解法,附详细注释、原理分析和复杂度说明,新手可直接复制代码运行,快速掌握核心解题思路。

LeetCode 1791.找出星型图的中心节点

1791. 找出星型图的中心节点

有一个无向的 星型 图,由 n 个编号从 1 到 n 的节点组成。星型图有一个 中心 节点,并且恰有 n - 1 条边将中心节点与其他每个节点连接起来。

给你一个二维整数数组 edges ,其中 edgesi = ui, vi 表示在节点 ui 和 vi 之间存在一条边。请你找出并返回 edges 所表示星型图的中心节点。

这道题比较简单

解法一:找两条边的公共节点

核心思路:星型图中,任意两条边的公共节点就是中心节点(因为中心节点与所有节点相连,必然出现在每一条边上)。无需遍历所有边,只需取前两条边,统计节点出现次数,出现2次的即为中心。

cpp 复制代码
class Solution {
public:
    int findCenter(vector<vector<int>>& edges) {
        int res = 0;
        unordered_map<int, int> count;
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                count[edges[i][j]]++;
                if (count[edges[i][j]] == 2) {
                    res = edges[i][j];
                }
            }
        }
        return res;
    }
};

复杂度:时间复杂度:O(1);空间复杂度:O(1)。

解法二:找度为n-1的节点

核心思路:星型图中,中心节点的度数(相连边的数量)为n-1(n为节点总数),其余节点度数均为1。先统计所有节点的度数,找到度数为n-1的节点即可。

注意:节点编号从1开始,因此度数数组需定义为n+1,避免下标越界

cpp 复制代码
class Solution {
public:
    int findCenter(vector<vector<int>>& edges) {
        // 计算节点的度
        // 只有中心节点的度为n-1
        int n = edges.size() + 1;
        vector<int> degrees(n + 1);
        // 遍历每条边
        for (auto& edge : edges) {
            // 两个节点的度+1
            degrees[edge[0]]++;
            degrees[edge[1]]++;
        }
        // 寻找度为n-1的数字
        for (int i = 0;; i++) {
            if (degrees[i] == n - 1) {
                return i;
            }
        }
        return 0;
    };
};

复杂度:时间复杂度:O(n);空间复杂度:O(n)。


LeetCode 463.岛屿的周长

463. 岛屿的周长

给定一个 row x col 的二维网格地图 grid ,其中:gridij = 1 表示陆地, gridij = 0 表示水域。

网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。

岛屿中没有"湖"("湖" 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。

解法一:迭代-嵌套循环

遍历每一个点,对陆地的边进行判断:上下左右四个方向的格子超边界/是水域→该方向的边就是周长的一部分

一般写法(直观易懂)

cpp 复制代码
class Solution {
public:
    int Count(vector<vector<int>>& grid, int i, int j) {
        int row = grid.size();
        int col = grid[0].size();
        int count = 0;
        // 上下左右是边界/是水域→边算作周长的一部分
        // 上
        if (i == 0 || grid[i - 1][j] == 0)
            count++;
        // 下
        if (i == row - 1 || grid[i + 1][j] == 0)
            count++;
        // 左
        if (j == 0 || grid[i][j - 1] == 0)
            count++;
        // 右
        if (j == col - 1 || grid[i][j + 1] == 0)
            count++;
        return count;
    }
    int islandPerimeter(vector<vector<int>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        int count = 0;
        // 嵌套循环每一个点
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == 1) { // 若是陆地→进行判断
                    count += Count(grid, i, j);
                }
            }
        }
        return count;
    }
};

更优写法(方向数组)

cpp 复制代码
class Solution {
public:
    // 方向数组→便于进行点的"移动"
    const int dx[4] = {0, 1, 0, -1};
    const int dy[4] = {1, 0, -1, 0};
    int islandPerimeter(vector<vector<int>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        int count = 0;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j]) { // 如果是陆地则进行判断
                    for (int k = 0; k < 4; k++) {
                        int x = i + dx[k];
                        int y = j + dy[k];
                        // 边界值/水域→周长+1
                        if (x < 0 || x >= row || y < 0 || y >= col || !grid[x][y]) {
                            count++;
                        }
                    }
                }
            }
        }
        return count;
    }
};

复杂度:时间复杂度:O(nm);空间复杂度:O(1)。

解法二:递归 DFS

递归的思路并不难,就是顺着一个点,继续向它的相邻陆地遍历,再顺着相邻陆地遍历.....

难的是如何避免重复遍历

可以通过修改gridij为2来表示已被遍历过

cpp 复制代码
class Solution {
public:
    // 方向数组→便于进行点的"移动"
    const int dx[4] = {0, 1, 0, -1};
    const int dy[4] = {1, 0, -1, 0};
    int dfs(vector<vector<int>>& grid, int x, int y) {
        int row = grid.size();
        int col = grid[0].size();
        // 边界值/水域→+1
        if (x < 0 || x >= row || y < 0 || y >= col || !grid[x][y]) {
            return 1;
        }
        if (grid[x][y] == 2) {
            return 0; // 已被遍历
        }
        grid[x][y] = 2; // 标记为被遍历
        int count = 0;
        // 遍历上下左右方向的点
        for (int i = 0; i < 4; i++) {
            int tx = x + dx[i];
            int ty = y + dy[i];
            count += dfs(grid, tx, ty);
        }
        return count;
    }

    int islandPerimeter(vector<vector<int>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        int count = 0;
        int x = 0, y = 0;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j]) { // 找到第一个岛屿,顺着遍历即可
                    count += dfs(grid, i, j);
                    // 岛屿是一个或多个表示陆地的格子相连,所以不存在单独的陆地
                    // 只需要找到一个陆地即可
                    break;
                }
            }
        }
        return count;
    }
};

复杂度:时间复杂度:O(nm);空间复杂度:O(nm)。

时间复杂度只看:总共遍历了多少个格子,不管 DFS 还是 迭代,遍历的格子数量是一模一样的,所以时间复杂度一样。


LeetCode 733.图像渲染

733. 图像渲染

有一幅以 m x n 的二维整数数组表示的图画 image ,其中 imageij 表示该图画的像素值大小。你也被给予三个整数 sr , sc 和 color 。你应该从像素 imagesrsc 开始对图像进行上色 填充 。

为了完成 上色工作:

从初始像素开始,将其颜色改为 color。

对初始坐标的 上下左右四个方向上 相邻且与初始像素的原始颜色同色的像素点执行相同操作。

通过检查与初始像素的原始颜色相同的相邻像素并修改其颜色来继续 重复 此过程。

当 没有 其它原始颜色的相邻像素时 停止 操作。

最后返回经过上色渲染 修改 后的图像 。

解法一:DFS

核心思路:先记录起点的原始颜色,若原始颜色与目标颜色相同,直接返回(避免无限递归);否则递归遍历起点的上下左右四个方向,将符合条件(在边界内、颜色等于原始颜色)的像素染色,并继续递归。

关键点:只遍历范围合法且颜色等于原始颜色的像素,即 if (x >= 0 && x < m && y >=0 && y < n && imagexy == oldcolor)才递归

cpp 复制代码
class Solution {
public:
    // 方向数组
    const int dx[4] = {0, 1, 0, -1};
    const int dy[4] = {1, 0, -1, 0};
    // vector<vector<int>> flag = {0};// 标记数组:值为1→已被遍历
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc,
                                  int color) {
        int m = image.size();
        int n = image[0].size();
        // 记录原始颜色
        int oldcolor = image[sr][sc];
        // 递归结束条件:颜色已经是新颜色
        // 防止无限递归
        if (oldcolor == color) {
            return image;
        }
        // 改变初始像素点颜色
        image[sr][sc] = color;
        // 遍历上下左右四个方向的像素
        for (int i = 0; i < 4; i++) {
            int x = sr + dx[i];
            int y = sc + dy[i];
            // 只遍历范围合法且颜色等于原始颜色的像素
            if (x >= 0 && x < m && y >=0 && y < n && image[x][y] == oldcolor) {
                floodFill(image, x, y, color);
            }
        }
        return image;
    }
};

复杂度

m、n分别为数组的行数、列数

时间复杂度:O(m*n)。所有像素点的颜色都等于 oldcolor 时,代码会遍历并修改每一个像素点,此时处理的像素点数量等于图像总像素数

空间复杂度:O(m*n)。递归调用栈的深度最坏为m*n(当图像为长条状或方阵时)

解法二:BFS

需要借助队列来实现:队列保存待遍历的点

查看初始像素的上下左右四个方向的像素,如果符合条件,将其放入队列

依次获取队列中的像素,查看其上下左右四个方向的像素是否符合条件,重复操作

BFS,顾名思义,向外扩散,将当前像素点作为起点,不断向外遍历像素,并做相应判断和操作

并且,广度优先搜索,需要使用到队列

相关队列操作

  1. 创建队列:queue<...> que;
  2. 入队:que.emplace()
  3. 队头元素:que.first()
  4. 出队:que.pop()

因为涉及二维数组,队列一次需要保存两个元素,所以可以用pair作为队列类型,其中first、second分别表示对是第一、二元素,即像素的行数和列数

cpp 复制代码
class Solution {
public:
    const int dx[4] = {1, 0, 0, -1};
    const int dy[4] = {0, 1, -1, 0};
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc,
                                  int color) {
        int oldcolor = image[sr][sc];
        if (oldcolor == color) {
            return image;
        }
        int m = image.size();
        int n = image[0].size();
        // 创建队列
        queue<pair<int, int>> que;
        que.emplace(sr, sc);      // 初始像素坐标入队
        image[sr][sc] = color; // 改变颜色
        while (!que.empty()) {
            // 获取队头原始的坐标
            int x = que.front().first;
            int y = que.front().second;
            que.pop(); // 队头元素出队
            // 遍历上下左右四个方向的像素
            for (int i = 0; i < 4; i++) {
                int tx = x + dx[i];
                int ty = y + dy[i];
                if (tx >= 0 && tx < m && ty >= 0 && ty < n &&
                    image[tx][ty] == oldcolor) {
                    que.emplace(tx, ty);      // 符合条件的入队
                    image[tx][ty] = color; // 同时改变颜色
                }
            }
        }
        return image;
    }
};

复杂度

m、n分别为数组的行数、列数

时间复杂度:O(m*n)。所有像素点的颜色都等于oldcolor时,代码会遍历并修改每一个像素点,此时处理的像素点数量等于图像总像素数

空间复杂度:O(m*n)。队列最多需要存储所有像素

总结

这3道题均为LeetCode简单题,覆盖「图基础」「二维数组遍历」「DFS/BFS」三大核心考点,适合新手入门练习,总结几个关键技巧:

  1. 星型图问题:抓住"中心节点是所有边的公共节点",无需遍历所有边,提升效率;

  2. 二维数组边界判断:优先使用方向数组,简化代码,避免重复判断;

  3. DFS/BFS选择:简单场景用DFS(代码简洁),大规模场景用BFS(避免栈溢出);

  4. 避免重复遍历:DFS用"修改原数组标记",BFS用"入队前染色",无需额外标记数组。

相关推荐
江畔柳前堤5 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore6 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考7 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX8 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面9 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号9 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽9 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI9 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者11 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者11 小时前
【征程6】校准量化中HistogramObserver解析
算法