题目描述
创建无向图类,存储结构使用邻接链表,提供操作:插入一条边,删除一条边,BFS,DFS。
输入输出格式
输入
第一行四个整数 n,m,s,t。n (10≤n≤100000) 代表图中点的个数,m (10≤m≤200000) 代表接下来共有 m 个操作,s 代表起始点,t 代表终点。
接下来 m 行,每行代表一次插入或删除边的操作,操作格式为:
-
0 u v
在点 u 和 v 之间增加一条边 -
1 u v
删除点 u 和 v 之间的边
输出
第一行输出图中有多少个连通分量
第二行输出所有连通子图中最小点的编号(升序),编号间用空格分隔
第三行输出从 s 点开始的 dfs 序列长度
第四行输出从 s 点开始的字典序最小的 dfs 序列
第五行输出从 t 点开始的 bfs 序列的长度
第六行输出从 t 点开始字典序最小的 bfs 序列
第七行输出从 s 点到 t 点的最短路径,若是不存在路径则输出-1
数据结构与算法描述
创建无向图类,私有成员有点和边的个数、邻接链表。公有成员中:
插入一条边的函数,先一个终点的节点,然后找与起点相连的点,如果没有则直接连接,如果有,则找一个与起点相连的大于终点的点,将终点插在该点前面,保证字典序从小到大。因为是无向图,将终点和起点交换再重复上面的过程。
void graph::insert(int u, int v) {//插入一条边
chainNode* insertnode = new chainNode(v);//创造一个终点的节点
chainNode* nextnode = list[u].next;//与起点相连的点
//按大小连接点,方柏霓按字典序输出
if (nextnode == NULL) {//没有相连的点,直接连
list[u].next = insertnode;
}
else if (nextnode->element >= v) {//如果相连的点大于终点
list[u].next = insertnode;//将较小的终点连在该点前面
insertnode->next = nextnode;
}
else {
chainNode* pronextnode = nextnode->next;//从下一个相连的点找
while(pronextnode != NULL && pronextnode->element < v) {//找到比终点大的点
nextnode = nextnode->next;
pronextnode = pronextnode->next;
}
nextnode->next = insertnode;//将较小的终点连在该点前面
insertnode->next = pronextnode;
}
chainNode* insertnode1 = new chainNode(u);//因为是无向图,所以反向再来一遍
chainNode* nextnode1 = list[v].next;
if (nextnode1 == NULL) {
list[v].next = insertnode1;
}
else if (nextnode1->element >= u) {
list[v].next = insertnode1;
insertnode1->next = nextnode1;
}
else {
chainNode* pronextnode1 = nextnode1->next;
while (pronextnode1 != NULL && pronextnode1->element < u) {
nextnode1 = nextnode1->next;
pronextnode1 = pronextnode1->next;
}
nextnode1->next = insertnode1;
insertnode1->next = pronextnode1;
}
numedges++;//边数加一
}
删除一条边的函数:从与起点相连的第一个点开始找,找到后将邻接链表的该节点删除,并将其他与起点相连的点接上,保持链表。因为是无向图,将终点和起点交换再重复上面的过程。
void graph::erase(int u, int v) {//删除点u和v之间的边
chainNode* erasenode = list[u].next;//从起点相连的第一个点开始找
if (erasenode->element == v ) {//如果是第一个
list[u].next = erasenode->next;
delete erasenode;
}
else {
chainNode* proerasenode = erasenode->next;//从下一个相连的点找
while(proerasenode->element != v) {//找到要删除的终点
erasenode = erasenode->next;
proerasenode = proerasenode->next;
}
erasenode->next = proerasenode->next;
delete proerasenode;
}
chainNode* erasenode1 = list[v].next;//因为是双向所以反过来再删除一次
if (erasenode1->element == u) {
list[v].next = erasenode1->next;
delete erasenode1;
}
else {
chainNode* proerasenode1 = erasenode1->next;
while(proerasenode1->element != u) {
erasenode1 = erasenode1->next;
proerasenode1 = proerasenode1->next;
}
erasenode1->next = proerasenode1->next;
delete proerasenode1;
}
numedges--;//边数减一
}
dfs递归函数,先将该点标记为已到达,然后找到与该点相连的点,进入循环,循环截止条件是没有与该点相连的点,在循环中如果该点没到达,则递归。
void dfs(int s) {//深度优先搜索
reach[s] = 0;//标记为已到达
chainNode* nextnode = list[s].next;//与s点相连的点
while (nextnode) {
if (reach[nextnode->element] != 0) {//如果没有到达
dfs(nextnode->element);//找该点相连的点
}
nextnode = nextnode->next;//下一个与s相连的点
}
}
Dfs的输出函数、dfs的长度函数,都是在每一次递归的时候进行输出或者长度加一。
void ldfs(int s) {//计算长度
length++;
reach[s] = 0;//标记为已到达
chainNode* nextnode = list[s].next;//与s点相连的点
while (nextnode) {
if (reach[nextnode->element] != 0) {//如果没有到达
ldfs(nextnode->element);//找该点相连的点
}
nextnode = nextnode->next;//下一个与s相连的点
}
}
void outdfs(int s) {//输出从s点开始的字典序最小的dfs序列
reach[s] = 0;//标记为已到达
cout << s;
chainNode* nextnode = list[s].next;//与s点相连的点
while (nextnode) {
if (reach[nextnode->element] != 0) {//如果没有到达
cout << " ";
outdfs(nextnode->element);//找该点相连的点
}
nextnode = nextnode->next;//下一个与s相连的点
}
}
统计连通分量个数,由于每一次dfs递归函数的调用就会形成一个连通分支,则每经过一次dfs且判断点是否已到达,统计个数就加一。Bfs和dfs的长度相等。
void component() {//统计连通分量个数
int num = 0;
for (int i = 1; i <= numvertices; i++) {
if (reach[i] != 0) {//一次深深度优先搜索成一个连通树
dfs(i);
num++;
}
}
cout << num << endl;
}
输出连通子图的最小点编号函数,同上一个函数,将统计个数加一,改为输出。
void subgraph() {//输出连通子图的最小点编号
for (int i = 1; i <= numvertices; i++) {
if (reach[i] != 0) {//一次深度优先搜索形成一个连通树
cout << i << " ";
dfs(i);
}
}
cout << endl;
}
Bfs函数,利用队列先进先出,则=先将起点压入队列中,并标记已到达,进入循环,知道队列为空循环结束,先将队列中的数取出并输出删除,然后将该点所有相连且没到达的点压入队列中。
void graph::bfs(int t) {//从t点开始字典序最小的bfs序列
queue q(numvertices);
q.push(t);
reach[t]=0;//标记已到达
while (!q.empty()) {
int w=q.front();//从队列中取出并输出
q.pop();
cout << w << " ";
for(chainNode* u = list[w].next;u!=NULL;u=u->next){//将该点所有相连且没到达的点压入队列中
if (reach[u->element] != 0) {
q.push(u->element);
reach[u->element] = 0;//标记为已经到达
}
}
}
cout << endl;
}
寻找最短路径,因为是无向图且没有加权,所以可以用bfs。比bfs多一个数组,统计“层数”即路径长度,并都初始化为0。在将该点所有相连且没到达的点压入队列中的循环中,要先判断一下是否到终点,并且将“层数“改为上一层加一。
void graph::minpath(int s, int t) {//用bfs找最短路径
queue q(numvertices);
q.push(s);
reach[s] = 0;
int path[numvertices + 1];//统计“层数”即路径长度
for (int i = 1; i <= numvertices; i++) {
path[i] = 0;//所有点初始化
}
while (!q.empty()) {
int w = q.front();
q.pop();
for(chainNode* u = list[w].next;u!=NULL;u = u->next){//将该点所有相连且没到达的点压入队列中
if (reach[u->element] != 0) {
if (u->element == t) {//已经到终点则输出
cout << path[w] + 1<<endl;
return;
}
path[u->element] = path[w] + 1;//该节点的“层数”为上一层加一
q.push(u->element);
reach[u->element] = 0;//标记为已经到达
}
}
}
cout << -1 << endl;
}
完整代码(含注释)
#include<iostream>
using namespace std;
struct chainNode {
int element;//节点的元素
chainNode* next;//指向下一节点的指针
chainNode() {
}
chainNode(const int& element) {
this->element = element;
this->next = NULL;
}
chainNode(const int& element, chainNode* next) {
this->element = element;
this->next = next;
}
};
class queue {
private:
chainNode* queuefront;//第一个数前一个位置的索引
chainNode* queueback;//最后一个数的索引
int queuelength;//队列长度
public:
queue(int n) {
queuelength = 0;
queuefront = queueback = NULL;
}
bool empty()const { return (queuefront)?false:true; }//是否为空
int front() { return queuefront->element; }//第一个位置
void pop() {//删除
chainNode* deletenode = queuefront->next;
delete queuefront;
queuefront = deletenode;
queuelength--;
}
void push(int theelement) {//插入
chainNode* newnode = new chainNode(theelement);
if (queuelength == 0) {
queuefront = newnode;
queueback = newnode;
}
else {
queueback->next = newnode;
queueback = newnode;
}
queuelength++;
}
};
int reach[1000000]={1};
int length = 0;
class graph {
private:
int numvertices;//点的个数
int numedges;//边的个数
chainNode* list;//邻接链表
public:
graph(int n) {
numvertices = n;
numedges = 0;
list = new chainNode[n + 1];
for (int i = 1; i <= n; i++) {
list[i].element = i;
list[i].next = NULL;
}
}
void insert(int u, int v);
void erase(int u, int v);
void component() {//统计连通分量个数
int num = 0;
for (int i = 1; i <= numvertices; i++) {
if (reach[i] != 0) {//一次深深度优先搜索成一个连通树
dfs(i);
num++;
}
}
cout << num << endl;
}
void subgraph() {//输出连通子图的最小点编号
for (int i = 1; i <= numvertices; i++) {
if (reach[i] != 0) {//一次深度优先搜索形成一个连通树
cout << i << " ";
dfs(i);
}
}
cout << endl;
}
void dfs(int s) {//深度优先搜索
reach[s] = 0;//标记为已到达
chainNode* nextnode = list[s].next;//与s点相连的点
while (nextnode) {
if (reach[nextnode->element] != 0) {//如果没有到达
dfs(nextnode->element);//找该点相连的点
}
nextnode = nextnode->next;//下一个与s相连的点
}
}
void ldfs(int s) {//计算长度
length++;
reach[s] = 0;//标记为已到达
chainNode* nextnode = list[s].next;//与s点相连的点
while (nextnode) {
if (reach[nextnode->element] != 0) {//如果没有到达
ldfs(nextnode->element);//找该点相连的点
}
nextnode = nextnode->next;//下一个与s相连的点
}
}
void outdfs(int s) {//输出从s点开始的字典序最小的dfs序列
reach[s] = 0;//标记为已到达
cout << s;
chainNode* nextnode = list[s].next;//与s点相连的点
while (nextnode) {
if (reach[nextnode->element] != 0) {//如果没有到达
cout << " ";
outdfs(nextnode->element);//找该点相连的点
}
nextnode = nextnode->next;//下一个与s相连的点
}
}
void bfs(int t);
void minpath(int s, int t);
};
void graph::insert(int u, int v) {//插入一条边
chainNode* insertnode = new chainNode(v);//创造一个终点的节点
chainNode* nextnode = list[u].next;//与起点相连的点
//按大小连接点,方柏霓按字典序输出
if (nextnode == NULL) {//没有相连的点,直接连
list[u].next = insertnode;
}
else if (nextnode->element >= v) {//如果相连的点大于终点
list[u].next = insertnode;//将较小的终点连在该点前面
insertnode->next = nextnode;
}
else {
chainNode* pronextnode = nextnode->next;//从下一个相连的点找
while(pronextnode != NULL && pronextnode->element < v) {//找到比终点大的点
nextnode = nextnode->next;
pronextnode = pronextnode->next;
}
nextnode->next = insertnode;//将较小的终点连在该点前面
insertnode->next = pronextnode;
}
chainNode* insertnode1 = new chainNode(u);//因为是无向图,所以反向再来一遍
chainNode* nextnode1 = list[v].next;
if (nextnode1 == NULL) {
list[v].next = insertnode1;
}
else if (nextnode1->element >= u) {
list[v].next = insertnode1;
insertnode1->next = nextnode1;
}
else {
chainNode* pronextnode1 = nextnode1->next;
while (pronextnode1 != NULL && pronextnode1->element < u) {
nextnode1 = nextnode1->next;
pronextnode1 = pronextnode1->next;
}
nextnode1->next = insertnode1;
insertnode1->next = pronextnode1;
}
numedges++;//边数加一
}
void graph::erase(int u, int v) {//删除点u和v之间的边
chainNode* erasenode = list[u].next;//从起点相连的第一个点开始找
if (erasenode->element == v ) {//如果是第一个
list[u].next = erasenode->next;
delete erasenode;
}
else {
chainNode* proerasenode = erasenode->next;//从下一个相连的点找
while(proerasenode->element != v) {//找到要删除的终点
erasenode = erasenode->next;
proerasenode = proerasenode->next;
}
erasenode->next = proerasenode->next;
delete proerasenode;
}
chainNode* erasenode1 = list[v].next;//因为是双向所以反过来再删除一次
if (erasenode1->element == u) {
list[v].next = erasenode1->next;
delete erasenode1;
}
else {
chainNode* proerasenode1 = erasenode1->next;
while(proerasenode1->element != u) {
erasenode1 = erasenode1->next;
proerasenode1 = proerasenode1->next;
}
erasenode1->next = proerasenode1->next;
delete proerasenode1;
}
numedges--;//边数减一
}
void graph::bfs(int t) {//从t点开始字典序最小的bfs序列
queue q(numvertices);
q.push(t);
reach[t]=0;//标记已到达
while (!q.empty()) {
int w=q.front();//从队列中取出并输出
q.pop();
cout << w << " ";
for(chainNode* u = list[w].next;u!=NULL;u=u->next){//将该点所有相连且没到达的点压入队列中
if (reach[u->element] != 0) {
q.push(u->element);
reach[u->element] = 0;//标记为已经到达
}
}
}
cout << endl;
}
void graph::minpath(int s, int t) {//用bfs找最短路径
queue q(numvertices);
q.push(s);
reach[s] = 0;
int path[numvertices + 1];//统计“层数”即路径长度
for (int i = 1; i <= numvertices; i++) {
path[i] = 0;//所有点初始化
}
while (!q.empty()) {
int w = q.front();
q.pop();
for(chainNode* u = list[w].next;u!=NULL;u = u->next){//将该点所有相连且没到达的点压入队列中
if (reach[u->element] != 0) {
if (u->element == t) {//已经到终点则输出
cout << path[w] + 1<<endl;
return;
}
path[u->element] = path[w] + 1;//该节点的“层数”为上一层加一
q.push(u->element);
reach[u->element] = 0;//标记为已经到达
}
}
}
cout << -1 << endl;
}
int main() {
int n, m, s, t;
cin >> n >> m >> s >> t;
graph g(n);
for (int i = 0; i < m; i++) {
int x, u, v;
cin >> x >> u >> v;
if (x == 0) {
g.insert(u, v);
}
else if (x == 1) {
g.erase(u, v);
}
}
for (int i = 1; i <= n; i++) {
reach[i] = 1;
}
g.component();
for (int i = 1; i <= n; i++) {
reach[i] = 1;
}
g.subgraph();
for (int i = 1; i <= n; i++) {
reach[i] = 1;
}
g.ldfs(s);
cout << length << endl;
for (int i = 1; i <= n; i++) {
reach[i] = 1;
}
g.outdfs(s);
cout << endl;
for (int i = 1; i <= n; i++) {
reach[i] = 1;
}
length = 0;
g.ldfs(t);//dfs和bfs长度相同
cout << length << endl;
for (int i = 1; i <= n; i++) {
reach[i] = 1;
}
g.bfs(t);
for (int i = 1; i <= n; i++) {
reach[i] = 1;
}
g.minpath(s, t);
return 0;
}