描述
创建无向图类,存储结构使用邻接链表,提供操作:插入一条边,删除一条边,BFS,DFS。
格式
输入
第一行四个整数n,m,s,t。n (10 \leq n \leq 10000010≤n≤100000) 代表图中点的个数,m (10 \leq m \leq 20000010≤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
样例
输入
10 20 4 5
0 6 4
0 10 3
0 4 8
0 4 10
1 4 10
0 2 1
0 5 8
0 5 2
0 10 7
0 9 6
0 9 1
0 7 1
0 8 10
0 7 5
0 8 3
0 6 7
1 6 4
1 8 3
0 7 8
0 9 2
输出
1
1
10
4 8 5 2 1 7 6 9 10 3
10
5 2 7 8 1 9 6 10 4 3
2
限制
1s, 10240KiB for each test case.
#include <iostream>
#include <string>
#include <cstring>
#include <queue>
#define MNum 100003 //最大顶点数;
using namespace std;
bool vist[MNum]; //顶点标记数组;
int ff = 0;
typedef struct ANode
{ //定义边结点;
int adv; //该边所指向的顶点的位置;
struct ANode *next; //指向下一条边的指针;
} ArcNode;
typedef struct VNode
{ //定义表头结点;
ANode *firstNode; //指向第一条依附于该表头的指针;
int element; //存放定点信息;
} AdVList;
struct ALGraph
{ //邻接表;
AdVList VLise[MNum]; //创建有MNum个结点的图;
int Vnum, Anum; //图的定点数和边数;
};
class Graph
{
public:
// 构造函数
Graph(int n = 100)
{
G.Vnum = n; // 确定图的定点个数
for (int i = 0; i < n; i++) // 建立一个指针数组,用于安排定点的存放
{
G.VLise[i].element = i + 1;
G.VLise[i].firstNode = NULL;
}
}
void bfsCounter(int n); // bfs长度
void insert(int v1, int v2); // 插入
void erase(int v1, int v2); // 删除
void bfs(int n); // bfs最小序列
void dfs(int n); // dfs最小序列
void fdfs(int n); // 计算连通分量函数的辅助函数
void components(int n); // 计算连同分量
void dfsCounter(int n); // dfs长度
void path2(int x, int y); // 最短路径求解
void everyComponents(int n); // 每个连同分量的最小元素
protected:
ALGraph G;
};
// 添加边
void Graph::insert(int v1, int v2)
{
int j, k;
j = v1 - 1; // 找到节点的位置
k = v2 - 1;
ANode *p1 = new ANode; // 定义两个边表节点
ANode *p2 = new ANode;
p1->adv = k;
ANode *p = G.VLise[j].firstNode;
ANode *pp = NULL;
if (p == NULL || p->adv > k)
{
p1->next = G.VLise[j].firstNode;
G.VLise[j].firstNode = p1;
}
else
{
while (p && p->adv < k)
{
pp = p;
p = p->next;
}
if (!p)
{
pp->next = p1;
p1->next = NULL;
}
else
{
p1->next = pp->next;
pp->next = p1;
}
}
ANode *q1 = new ANode;
ANode *q2 = new ANode;
q1->adv = j;
ANode *q = G.VLise[k].firstNode;
ANode *qq = NULL;
if (q == NULL || q->adv > j)
{ // 插入节点并对节点的顺序进行从小到大排序
q1->next = G.VLise[k].firstNode; // (头插法)
G.VLise[k].firstNode = q1;
}
else
{
while (q && q->adv < j)
{ // 查找比插入的节点大的值
qq = q;
q = q->next;
}
if (!q)
{ // 查到最后一个节点(末尾插入)
qq->next = q1;
q1->next = NULL;
}
else
{ // 没有查到最后的节点(中间插入)
q1->next = qq->next;
qq->next = q1;
}
}
G.Anum++; // 边数加一
}
// 删除边
void Graph::erase(int v1, int v2)
{
v1 = v1 - 1; // 找到节点的位置
v2 = v2 - 1;
ANode *current = G.VLise[v2].firstNode; // 找到其中一个定点的头结点
ANode *trail = NULL;
while (current != NULL && current->adv != v1)
{ // 寻找需要删除的边的另一个节点
trail = current;
current = current->next;
}
if (current == NULL)
{ // 需要删除的边不存在
cout << "none" << endl;
return;
}
if (trail != NULL) // 判断删除的是否是头结点(不是头结点)
trail->next = current->next;
else // 头结点
G.VLise[v2].firstNode = current->next;
delete current; // 删除节点
// 同理实现另一个节点方面的删除
ANode *current2 = G.VLise[v1].firstNode;
ANode *trail2 = NULL;
while (current2 != NULL && current2->adv != v2)
{
trail2 = current2;
current2 = current2->next;
}
if (current2 == NULL)
{
cout << "none" << endl;
return;
}
if (trail2 != NULL)
trail2->next = current2->next;
else
G.VLise[v1].firstNode = current2->next;
delete current2;
G.Anum--; // 边数减一
}
// dfs
void Graph::dfs(int n)
{
cout << G.VLise[n].element; // 输出起点的值
vist[n] = false; // 对输出的值进行标记
ANode *p = G.VLise[n].firstNode; // 定义一个指针在该链表遍历
while (p) // 只要p不为空就继续执行
{
if (vist[p->adv]) // 判断是否已经输出
{
cout << " ";
dfs(p->adv); // 递归(深度遍历)
}
p = p->next; // 节点后移
}
}
void Graph::fdfs(int n)
{
vist[n] = false;
ANode *p = G.VLise[n].firstNode;
while (p)
{
if (vist[p->adv])
{
fdfs(p->adv);
}
p = p->next;
}
}
void Graph::dfsCounter(int n)
{
vist[n] = false;
ANode *p = G.VLise[n].firstNode;
ff++; // 进行长度的记录
while (p)
{
if (vist[p->adv])
{
dfsCounter(p->adv);
}
p = p->next;
}
}
// bfs
void Graph::bfs(int n)
{
queue<int> q; // 定义一个队列
q.push(n); // 将起点压入队列中
while (!q.empty())
{
cout << G.VLise[q.front()].element << " "; // 输出队首
vist[q.front()] = false; // 标记队首已输出
ANode *p = G.VLise[q.front()].firstNode;
while (p) // 将p指针指向的节点的链表全部压入队列中
{
if (vist[p->adv])
{
vist[p->adv] = false;
q.push(p->adv);
}
p = p->next;
}
q.pop(); // 删除队首
}
cout << endl;
}
// 连通分量
void Graph::components(int n)
{
memset(vist, true, sizeof(vist));
int i, flag = 0;
for (i = 0; i < n; i++)
{
if (vist[i] == true) // 使用dfs的方法,进行链表上的节点标记,当有节点在某一条节点上标记时,连通分量加一
{
fdfs(i);
flag++;
}
}
cout << flag << endl;
memset(vist, true, sizeof(vist)); // 最后将所有节点的标记去除
}
void Graph::everyComponents(int n) // 同计算连通分量的方法一样(因为每一次插入都进行了排序,直接输出链表的第一个元素即可
{
memset(vist, true, sizeof(vist));
int i;
for (i = 0; i < n; i++)
{
if (vist[i] == true)
{
cout << i + 1 << " ";
fdfs(i);
}
}
memset(vist, true, sizeof(vist));
cout << endl;
}
void Graph::path2(int x, int y)
{
queue<int> q; // 队列
int num = G.Vnum + 1;
q.push(x); // 压入起点
vist[q.front()] = false; // 标记
int path[num]; // 定义一个数组(用于记录路径长度)
for (int i = 0; i < num; i++)
path[i] = 0; // 数组初始化
while (!q.empty()) // 在链表中查找时(查询直到链表末尾)
{
int w = q.front(); // 起点
q.pop();
ANode *p = G.VLise[w].firstNode;
while (p != NULL)
{
if (vist[p->adv]) // 如果没有被标记
// 一个节点的所有下一个节点路径长度都相同,所以可以实现最短路径的实现
{
if (p->adv == y) // 判读是否为终点
{
cout << path[w] + 1 << endl; // 输出长度
return;
}
path[p->adv] = path[w] + 1; // 下一个节点都在这个节点的基础上进行加一操作
q.push(p->adv); // 压入下一个节点
vist[p->adv] = false; // 标记
}
p = p->next;
}
}
cout << "-1" << endl;
}
void Graph::bfsCounter(int n)
{
queue<int> q; // 定义一个队列
q.push(n); // 将起点压入队列中
ff++; // 起点进行加一操作
while (!q.empty())
{
vist[q.front()] = false; // 标记队首已输出
ANode *p = G.VLise[q.front()].firstNode;
while (p) // 将p指针指向的节点的链表全部压入队列中
{
if (vist[p->adv])
{
vist[p->adv] = false;
q.push(p->adv);
ff++; // 压入一个就进行加一操作,进行计数
}
p = p->next;
}
q.pop(); // 删除队首
}
}
int main()
{
int n, s, t;
int m;
cin >> n >> m >> s >> t;
Graph A(n);
for (int i = 0; i < m; i++) // 结下来m行输入
{
int op;
cin >> op;
if (op == 0)
{
int v1, v2;
cin >> v1 >> v2;
A.insert(v1, v2);
}
else if (op == 1)
{
int v1, v2;
cin >> v1 >> v2;
A.erase(v1, v2);
}
}
A.components(n); // 第一行输出图中有多少个连通分量
A.everyComponents(n); // 第二行输出所有连通子图中最小点的编号(升序),编号间用空格分隔
A.dfsCounter(s - 1); // 第三行输出从s点开始的dfs序列长度(ff记录长度)
cout << ff << endl;
ff = 0;
memset(vist, true, sizeof(vist));
A.dfs(s - 1); // 第四行输出dfs最小序列
cout << endl;
memset(vist, true, sizeof(vist));
A.bfsCounter(t - 1);
cout << ff << endl; //第五行输出从t点开始的bfs序列的长度
ff = 0;
memset(vist, true, sizeof(vist));
A.bfs(t - 1); // 第六行输出bfs序列
memset(vist, true, sizeof(vist));
A.path2(s - 1, t - 1); // 第七行输出从s点到t点的最短路径,若是不存在路径则输出-1
return 0;
}