图的相关题

题目描述:

给定一个无向图 G 的邻接矩阵,顶点数为 n,顶点编号为从 0 到 n−1。判断图 G 的形状是否是一个首尾相接的环。

函数说明:

  • 2≤n≤1000
  • G 是一个 n 行 n 列的二维对称矩阵
  • G[i][j]=0 表示顶点 i 和顶点 j 之间不存在边
  • G[i][j]=1 表示顶点 i 和顶点 j 之间存在边
  • G[i][i]=0,即不会有指向自己的边

 方法一

/**
 * @param G: 邻接矩阵,表示无向图,可按二维数组方式G[i][j]访问内部元素
 * @param n: 图的顶点数
 * @return: 返回一个布尔值,表示图是否为环形
 */
bool isCircleGraph(int** G, int n) {
    for (int i = 0; i < n; ++i) {
        int degree = 0;   // 统计每个编号结点的度
        for (int j = 0; j < n; ++j) {
            if (G[i][j] == 1) {
                degree++;
            }
        }
        if (degree != 2) {return false;}
    }
    int i = 0, pre = -1;  // 不失一般性,从0号结点开始
    int cnt = 0,zerePassCnt = 0;
    while (zerePassCnt < 1) {
        for (int j = 0; j < n; ++j) {
            if (G[i][j] == 1 & j != pre) { // 存在邻接点 并且与不是前驱结点相邻的结点有边
                pre = i;
                i = j;
                break;
            }
        }
        cnt++;    // 走的步数,用来判断是否回到了起点
        if (i == 0) {zerePassCnt++;}
    }
    return cnt == n;
}

方法二(并查集) 

#include <iostream>
#include <vector>
using namespace std;
class Djset {
public:
    vector<int> parent;  // 记录节点的根
    vector<int> rank;  // 记录根节点的深度(用于优化)
    int count;
    Djset(int n): parent(vector<int>(n)), rank(vector<int>(n)), count(n) {
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }
 
    int find(int x) {
        // 压缩方式:直接指向根节点
        if (x != parent[x]) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }
 
    void merge(int x, int y) {
        int rootx = find(x);
        int rooty = find(y);
        if (rootx != rooty) {
            if (rank[rootx] < rank[rooty]) {
                swap(rootx, rooty);
            }
            parent[rooty] = rootx;
            if (rank[rootx] == rank[rooty]) rank[rootx] += 1;
            count--;
        }
    }
 
    int get_count() {
        return count;
    }
};
bool isCircleGraph(int** G, int n) {
    Djset uf(n);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (G[i][j]) {
                if (uf.find(i) != uf.find(j)) {
                    uf.merge(i,j);
                }
            }
        }
    }
    vector<int> degree(n,0);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (G[i][j]) {degree[i]++;}
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (uf.find(i) != uf.find(j) || degree[i] != 2) {
                return false;
            }
        }
    }
    return true;
}

 

题目描述

给定一个无向图 G 的邻接矩阵,顶点数为 n,顶点编号为从 0 到 n−1。判断图 G 的形状是否是 6 字形,即是否仅由一个环与一条链组成,且环上有且仅有一个顶点与链的其中一个首尾顶点相连。

思考:你可以用空间复杂度 O(1) 的做法吗?

函数说明

  • 3≤n≤1000
  • G 是一个 n 行 n 列的二维对称矩阵
  • G[i][j]=0 表示顶点 i 和顶点 j 之间不存在边
  • G[i][j]=1 表示顶点 i 和顶点 j 之间存在边
  • G[i][i]=0,即不会有指向自己的边
/**
 * @param G: 邻接矩阵,表示无向图,可按二维数组方式G[i][j]访问内部元素
 * @param n: 图的顶点数
 * @return: 返回一个布尔值,表示图是否为6字形
 */
bool isSixGraph(int** G, int n) {
    int oneDegreeIdx = -1, threeDegreeIdx = -1; // 度数为1和3的顶点下标
    for (int i = 0; i < n; i++) { // 遍历所有顶点
        int degree = 0; // 计算当前顶点的度数
        for (int j = 0; j < n; j++) {
            if (G[i][j] == 1) {
                degree++;
            }
        }
        if (degree == 1 && oneDegreeIdx == -1) {
            oneDegreeIdx = i; // 记录度数为1的顶点下标
        } else if (degree == 3 && threeDegreeIdx == -1) {
            threeDegreeIdx = i; // 记录度数为3的顶点下标
        } else if (degree != 2) {
            return false; // 度数不为2的顶点,直接返回false
        }
    }
    if (oneDegreeIdx == -1 || threeDegreeIdx == -1) {
        return false; // 如果没有找到度数为1或3的顶点,返回false
    }
    int i = oneDegreeIdx, pre = -1; // 从度数为1的顶点开始遍历
    int cnt = 0, threeDegreePassCnt = 0; // 计数器和度数为3顶点的通过次数
    while (threeDegreePassCnt < 2) { // 遍历顶点直到度数为3的顶点通过两次
        for (int j = 0; j < n; j++) {
            if (G[i][j] == 1 && j != pre) { // 找到下一个顶点
                pre = i;
                i = j;
                break;
            }
        }
        cnt++; // 计数器增加
        if (i == threeDegreeIdx) {
            threeDegreePassCnt++; // 记录度数为3顶点的通过次数
        }
    }
    return cnt == n; // 检查是否所有顶点都被遍历过
}

题目描述

给定一个无向图 G 的邻接矩阵,顶点数为 n,顶点编号为从 0 到 n−1。判断图 G 的形状是否是 8 字形,即是否仅由两个环组成,且其中一个环上有且仅有一个顶点与另一个环相连。

思考:你可以用空间复杂度 O(1) 的做法吗?

函数说明

  • 5≤n≤1000
  • G 是一个 n 行 n 列的二维对称矩阵
  • G[i][j]=0 表示顶点 i 和顶点 j 之间不存在边
  • G[i][j]=1 表示顶点 i 和顶点 j 之间存在边
  • G[i][i]=0,即不会有指向自己的边

 

/**
 * @param G: 邻接矩阵,表示无向图,可按二维数组方式G[i][j]访问内部元素
 * @param n: 图的顶点数
 * @return: 返回一个布尔值,表示图是否为8字形
 */
bool isEightGraph(int** G, int n) {
    int fourDegreeIdx = -1; // 度数为4的顶点下标
    for (int i = 0; i < n; i++) { // 遍历所有顶点
        int degree = 0; // 计算当前顶点的度数
        for (int j = 0; j < n; j++) {
            if (G[i][j] == 1) {
                degree++;
            }
        }
        if (degree == 4 && fourDegreeIdx == -1) {
            fourDegreeIdx = i; // 记录度数为4的顶点下标
        } else if (degree != 2) {
            return false; // 度数不为2的顶点,直接返回false
        }
    }
    if (fourDegreeIdx == -1) {
        return false; // 如果没有找到度数为4的顶点,返回false
    }
    // 从度数为4的顶点开始遍历,nextIdx记录第一次从起点出发后的下一个顶点
    int i = fourDegreeIdx, pre = -1, nextIdx = -1; // nextIdx 的作用是为了避免进入相同的环
    int cnt = 0, fourDegreePassCnt = 0; // cnt为总移动次数,fourDegreePassCnt为度数为4的顶点的通过次数
    while (fourDegreePassCnt < 2) { // 如果度为4的顶点到达了两次,那么退出遍历
        for (int j = 0; j < n; j++) {
            if (G[i][j] == 1 && j != pre && j != nextIdx) { // 找到下一个顶点(注意这里要避开pre和nextIdx)
                pre = i;
                i = j;
                if (nextIdx == -1) {    // 记录第一次出发时的下一个顶点
                    nextIdx = j;
                }
                break;
            }
        }
        cnt++; // 总移动次数加1
        if (i == fourDegreeIdx) {
            fourDegreePassCnt++; // 记录度数为4顶点的到达次数
        }
    }
    return cnt == n + 1; // 检查是否所有顶点都被遍历过
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值