大二数据结构课设总结--无向图的关节点问题

本文介绍了如何使用邻接表和Tarjan算法解决无向连通图的关节点问题,包括数据输入、深度优先遍历求解、查询统计、以及将关节点转换为非关节点的功能。作者还分享了使用深度优先搜索和around数组来实现节点改造的过程。
摘要由CSDN通过智能技术生成

无向图的关节点问题

【问题描述】

对无向连通图,若删除某个结点使其成为非连通图,则称该结点为关节点。假设某一地区公路交通网,求解关节点。

【设计要求】

设计求解无向连通图关节点的模拟程序。

(1)采用邻接表或邻接矩阵存储结构。

(2)可以随机、文件及人工输入数据。

(3)采用深度优先遍历求解关节点。

(4)实现关节点的查询和统计功能。

(5)实现将关节点改造为非关节的功能。

(6)其它完善性或扩展性功能。

最终实现功能

看题目,嗯?关节点,没听过,再一看,这不是割点吗?那我知道这个可以用Tarjan算法,唯独要求5,将关节点改造为非关节点的功能不太清楚。但是后面清楚了,功能基本上就是题目所写的那些。

收获

第一个是Tarjan算法本身吧

第二个是图论相关知识

之前写图论的题目基本上都是凭直觉在写,但是遇见了这个洛谷对应的题解的严谨证明让我对图论很感兴趣了,于是后来也额外学了一些图论知识。

没有要分享的有趣代码片段。

将关节点改为非关节点

一个节点想改为非关节点,那只需要把这个节点周围的连通块相连即可,所以考虑在dfs处理时使用around数组记录节点的周围连通块(记录直接相连的点即可),在修改的时候直接增边即可。

完整代码

可以稍微修改AC洛谷的Tarjan算法的模板题目。

#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <fstream>
#include <algorithm>

#define ll long long

using namespace std;

namespace MyGraph
{
    class my_graph
    {
    private:
        typedef unsigned long long size_t;

        vector<set<size_t>> graph;//为了防止输入重复边,使用set.一个简易的邻接表。
        size_t arcnum;
    public:
        my_graph(size_t x):arcnum(0){graph.resize(x+1);}
        ~my_graph(){graph.clear();arcnum=0;}

        const set<size_t>& get_arc(size_t x){return graph[x];}
        
        void resize(size_t x){graph.resize(x+1);}
        size_t getvnum(){return graph.size()-1;}
        size_t getarcnum(){return arcnum;}
        void add_edge(size_t x,size_t y){if(x==y||graph[x].count(y))return;graph[x].insert(y);++arcnum;}
    };
}

void dfs(int now,MyGraph::my_graph&graph,int low[],int is_key[],set<int>&key_point,int dfn[],int &root,int &cnt,vector<int> around[])
{
    dfn[now]=low[now]=++cnt;//时间戳
    int son=0;

    for(auto&it:graph.get_arc(now))
    {
        if(dfn[it]==0)
        {
            ++son;
            dfs(it,graph,low,is_key,key_point,dfn,root,cnt,around);

            //补充around,为了modify
            around[now].push_back(it);
            around[it].push_back(now);

            low[now]=min(low[now],low[it]);

            if(low[it]>=dfn[now]&&now!=root&&is_key[now]==0)
                key_point.insert(now),is_key[now]=1;
        }
        else
            low[now]=min(low[now],dfn[it]);
    }
    
    if(root==now&&son>=2&&is_key[now]==0)
        key_point.insert(now),is_key[now]=1;
}

void Tarjan(MyGraph::my_graph& graph,set<int>&key_point,vector<int> around[])
{
    int dfn[graph.getvnum()+1]={0},low[graph.getvnum()+1]={0},is_key[graph.getvnum()+1]={0};

    int root=0,cnt=0;

    for(int i=0;i<=graph.getvnum();++i)
    {
        if(dfn[i]==0)
        {
            root=i;
            dfs(i,graph,low,is_key,key_point,dfn,root,cnt,around);
        }
    }
}
 
//通过文件输入
void read_data_by_file(string &filename,MyGraph::my_graph &graph,int&query)
{
    // 打开文件
    ifstream infile(filename);
    if (!infile.is_open())
    {
        cout << "Error: Unable to open file." << endl;
        return;
    }

    int n, m; // 图的顶点数和边数
    infile >> n >> m;

    // 初始化图
    graph.resize(n);

    // 读入图的边信息
    for (int i = 0; i < m; ++i)
    {
        int x, y;
        infile >> x >> y;
        graph.add_edge(x, y);
        graph.add_edge(y, x); // 无向图需添加反向边
    }

    // 将新图赋值给传入的图对象

    // 读入查询次数
    infile >> query;
    
    // 关闭文件
    infile.close();
}


//通过手动输入
void read_data_by_input(MyGraph::my_graph&graph,int&query)
{
    int n, m; // 顶点数和边数
    cout << "please input point number and edge number :" << endl;
    cin >> n >> m;

    // 初始化图
    MyGraph::my_graph new_graph(n);

    // 读入图的边信息
    cout << "please input the two ends of every edge :" << endl;
    for (int i = 0; i < m; ++i)
    {
        int x, y;
        cin >> x >> y;
        new_graph.add_edge(x, y);
        new_graph.add_edge(y, x); // 无向图需添加反向边
    }

    // 将新图赋值给传入的图对象
    graph = new_graph;

    // 读入查询次数
    cout << "please input how many do you want to query :" << endl;
    cin >> query;
}

//把 x点修改为非割点
void modify(int x,MyGraph::my_graph& graph,vector<int> around[])
{
    for(int i=1;i<around[x].size();++i)
    {
        graph.add_edge(around[x][i],around[x][i-1]);
        graph.add_edge(around[x][i-1],around[x][i]);
    }
}

void solve()
{
    char op;
    cout<<"please input how do you want to input :(H is hand and F is file)"<<endl;
    cin>>op;

    MyGraph::my_graph graph(0);

    int query=0;//query为询问或者操作次数

    //手写输入 graph数据
    if(op=='H')
        read_data_by_input(graph,query);
    else
    {
        // 通过文件输入graph数据,其他的还是手动输入 
        string filename;
        cin>>filename;
        read_data_by_file(filename,graph,query);
    }

    //获得结果。res里面全都是割点
    set<int> res;
    vector<int> around[graph.getvnum()+1];//保存父亲和孩子,用于modify函数
    Tarjan(graph,res,around);//先初始化

    cout<< "let's start to query :"<<endl;

    while(query--)
    {
        cout<<endl;
        cout<<"Q is querying one point whether is key point or not"<<endl;
        cout<<"S is outputting all key points"<<endl;
        cout<<"M is modifying a key point to a not key point"<<endl;
        cout<<endl;

        cin>>op;
        int x;

        if(op=='Q')//询问x点是不是割点
        {
            cin>>x;

            cout<< (res.count(x)?"yes":"no")<<endl;
        }
        else if(op=='S')//输出所有割点
        {
            cout<<"the number of all key points is "<< res.size()<<" :"<<endl;
            for(auto&it:res)
                cout<<it<<' ';
            cout<<endl;
        }
        else if(op=='M')// 把某个点修改成非割点
        {
            cin>>x;

            //如果是,则修改
            if(res.count(x))
            {
                modify(x,graph,around);

                for(int i=0;i<=graph.getvnum();++i)
                    around[i].clear();
                
                res.clear();
                Tarjan(graph,res,around);
                cout<< "modify finished"<<endl;
            }
            else//如果不是,不修改
            {
                cout<< "this point is not key point" <<endl;
            }
        }
    }

}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    int t=1;
    //cin>>t;

    while(t--)
    {
        solve();
    }    


    cout<<endl;
    system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DogDu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值