简单算法-割点和割边

一、什么是割点和割边?

1、割点:当在一个连通的图中,去除某个点,使得该图不再连通,而被去除的点就是该图的割点。

2、割边(桥):如割点一样,在一个连通的图中,去除某条边,使得该图不再连通,而被去除的条边就是该图的割边,也称桥。

二、如何判断是否为割点和割边?

      在看文字的时候务必边画边看。

首先,我们需要引入一个时间轴(num)和连接轴(low)(实在时想不到啥名字了)。我们可以在脑海里想象一个图,1--2 2--3 3--4 4--2(无向图),我们从一端开始,假设它的编号为1,我们每次到达一个结点,都初始化num 和 low 当前时间  比如:我们从1结点开始  ,时间也从1开始,那么结点1的num 和 low都为1;结点2的num 和 low就为2。。。当结点来到4时,结点4指向的是2,此时,结点4的low 则改变为2;说明产生了回退边(该结点的孩子指向了当前结点之前)由于我们是通过DFS实现在回溯的同时更新前方已访问过的结点的low。这些步骤结束之后,开始直观判断割点,如果该结点的num < 孩子的low,说明改结点是割点 。 否则,说明孩子与比自己当前位置还前的结点有连接。所以,就算去除自己,孩子还是会通过另外一条路径与整个图相连。但值得注意的是开始的端点结点,它的时间最早,所以不管它的孩子的low值怎么变化 它的num 都小于孩子的low,  显然并不是所有的端点都是割点 ,对于这种情况我们用孩子数量的办法,当端点的孩子数量 多于1时说明是割点。

割边判断方法一样,当前结点的u的num < 孩子结点v的low时,且不需要判断开始点不需要另外判断,则割边为:u--v;

以下 是判断割点的代码:

#include <iostream>
#include <vector>
using namespace std;

vector<int>G[105];  //存边
int num[105], low[105];  //时间轴、连接轴。
int judge[105];       //判断是否为割点
int n, m, dfn;        //点和边  时间(dfn)

void dfs(int u,int father) {    //当前结点和双亲结点
	low[u] = num[u] = ++dfn;    //初始化时间轴和连接轴为当前时间。
	int child = 0;              //该结点的孩子  主要用于判断头结点是否为割点。
	for (int j = 0; j < G[u].size(); j++) {
		int v = G[u][j];
		if (!num[v]) {         //如果该结点未被访问
			child++;
			dfs(v, u);
			low[u] = min(low[u], low[v]);  //放在dfs()的后面,所以在此结点后的结点都已更新连接轴的值
			if (num[u] <= low[v] && u != 1) {     //如果当前结点(不是头结点)时间轴的值小于等于孩子的连接轴的值,则当前结点为割点
				judge[u] = true;
			}
		}
		else if (num[u] > num[v] && v != father) { //如果该结点已被访问过且当前结点u  的时间轴小于孩子的时间轴的值且孩子不是u的双亲,如果是双亲则没有任何意义这属于多重边
			low[u] = min(low[u], num[v]);   //更新u 连接轴的值
		}
	}
	if (u == 1 && child > 1)     //如果u == 1 时不能用上述程序实现,通过孩子数量来确定是否为割点。
		judge[1] = true;
}
int main() {
	int a, b;
	while (cin >> n >> m) {
		memset(low, 0, sizeof(low));
		memset(num, 0, sizeof(num));
		for (int i = 0; i <= n; i++)
			G[i].clear();
		dfn = 0;
		for (int i = 1; i <= m; i++) {
			cin >> a >> b;
			G[a].push_back(b);
			G[b].push_back(a);
		}
		dfs(1, -1);           //从1开始  双亲结点为-1.(属于初始化)
		cout << "割点有:";
		for (int i = 0; i <= n; i++) {
			if (judge[i]) {
				cout << i << " ";
			}
		}
		cout << endl;
	}
	system("pause");
	return 0;
}

一下为割边代码:

#include <iostream>
#include <vector>
#include <stack>
using namespace std;
#define Max 20
typedef struct Edge {
	int u, v;
}edge;
bool judge[Max];                                             //true 是割点  false 不是
vector<int>G[Max];                                          //存边
stack<Edge>s;
int num[Max], low[Max], dfn;                      //dfn来标记递归的层数;
edge* tail;
void dfs(int u, int fa) {                         //此次dfs的结点 和 父结点
	low[u] = num[u] = ++dfn;                     //开始的low 和 nun 是相等的
	int child = 0, v;                                //孩子个数   主要用于根结点判断割点
	for (int i = 0; i < G[u].size(); i++) {
		v = G[u][i];
		if (!num[v]) {                          //未访问过
			child++;
			dfs(v, u);
			low[u] = min(low[u], low[v]);         //从孩子的low 中更新 low
			if (num[u] < low[v])          //判断割点与这里不同;
			{
				tail = new Edge; tail->u = u; tail->v = v; s.push(*tail);
			}                  //注意:若这里的判断条件改为 num < low 就成为了判断割边的条件  (当然割边不能在用judge[]来存储)
		}
		else if (num[v] < num[u] && v != fa)      //对于已经访问过的点 必然存在 num  ,这时需要更新low , = 此孩子的num. 
			low[u] = min(low[u], num[v]);
	}
		
}
int main() {
	int N, M, from, to;
	cin >> N >> M;
	memset(judge, false, sizeof(judge));
	memset(low, 0, sizeof(low));
	memset(num, 0, sizeof(num));
	dfn = 0;
	for (int i = 1; i <= M; i++) {
		cin >> from >> to;
		G[from].push_back(to);
		G[to].push_back(from);
	}
	dfs(1, -1);
	edge *start;
	cout << "--------" << endl;
	while (s.size()) {
		start = &(s.top());
		s.pop();
		cout << start->u << "---" << start->v << endl;
	}
	cout << endl;
	system("pause");
	return 0;
}

                                                                                                ---请多指教

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值