Vj数据结构实验十二

描述

创建无向图类,存储结构使用邻接链表,提供操作:插入一条边,删除一条边,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;
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值