题目描述:
给定一个无向图 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; // 检查是否所有顶点都被遍历过
}