浙大数据结构ch5:邻接表与邻接矩阵实现图类及分别实现BFS & DFS(附离散数学图论相关内容)

7-32 哥尼斯堡的“七桥问题”,自己做两个测试点递归解决超时(焯:

#include <bits/stdc++.h>
using namespace std;
vector<int> path;
vector<vector<bool>> graph;
vector<vector<bool>> edge;
int n,m,v1,v2;
bool flag=false;
void dfs(int start,int v,int cnt){
    if (flag) return;
    //v是当前访问点,start是起点,edge是已经走过的边,cnt是走过边的数目
    if (cnt==m){
        if (start==v) flag=true;
        return;
    }
    //从这个点出发,尝试走那些没走过的边
    for (int w=0;w<n;w++){  //v->w有路并且没有走过
        if (graph[v][w] && !edge[v][w]){
            edge[v][w]=1;edge[w][v]=1;
            cnt++;
            dfs(start,w,cnt);
            edge[v][w]=0;edge[w][v]=0;
            cnt--;
        }
    }
}

int main(){
    //包含所有边且从起点回到自己的路径不重复
    scanf("%d %d",&n,&m);
    //dfs走遍所有所有长度为m的路径
    //记录走过边的情况和当前到达点的情况,判断是否存在
    graph.resize(n);
    for (int i=0;i<n;i++) graph[i].push_back(false);
    edge.resize(n);
    for (int i=0;i<n;i++) edge[i].push_back(false);
    for (int i=0;i<m;i++){
        scanf("%d %d",&v1,&v2);
        graph[v1-1][v2-1]=true;
        graph[v2-1][v1-1]=true;
    }
    //记录走过的边,每走过一条边就把两个点对应的边设为1
    for (int start=0;start<n;start++){
        dfs(start,start,0);
        if (flag){
            printf("1");
            return 0;
        }
        flag=false;
        edge.clear();
    }
    printf("0");
}

用定理判断:图连通 + 所有节点度数都是偶数

  • 如果需要记录 DFS 和 BFS 的步数,即从起始点到当前节点走过的步数:
    • DFS 可以加全局变量 step,回溯法处理
    • BFS 可以记录每层的最后一个节点,当访问每层的最后一个节点之后 step++
  • 图论相关内容:
    • 简单图: 每条边连接着两个不同的顶点(没有自回路);没有两条边连接着同一对顶点(没有重边)
    • 多重图:简单图的两条性质都可以打破
    • 简单路径:路径上的顶点都不相同
    • 同构图:两个图的顶点集存在一个一一映射 f,并且一个图中邻接的两个顶点在映射后仍然邻接
    • 图不变量:只和抽象图结构相关的属性,同构图的任何图不变量都相等,即若两个图的某个图不变量不相等,这两个图必然不同构。图不变量包括顶点数量,边数量,顶点度集合
    • 关节点/割点:移除该点和该点连接的边后,图的连通分量变多的那些点,边也有类似的概念,术语是
    • 点割集/分离集:移除分离集中的点之后图不再连通,边也有类似概念,术语是 边割集
    • 点连通度:点割集的最少点数。如果图的点连通度是 k,图是 j-点连通的(j<=k),边也有类似概念,连通度是图不变量
    • 欧拉回路/欧拉路径:包含所有边的简单回路/简单路径,点也有类似概念,术语是 哈密尔顿回路/哈密尔顿路径
    • 对于多重图:每个顶点度都是偶数    ⟺    \iff 至少有两个不同的点间有欧拉回路;有且仅有两个顶点度是奇数    ⟺    \iff 图有欧拉路径
    • 对于顶点数n >=3 的简单图:每个顶点的度>=n/2 ⇒ \Rightarrow 有哈密尔顿回路;任意不邻接的顶点 u 和 v,deg(u)+deg(v)>=n ⇒ \Rightarrow 有哈密尔顿回路
    • 一些图:
      • 完全图 K n K_n Kn:简单图且每个顶点都和其他n-1个顶点相连
        请添加图片描述

      • C n C_n Cn:顶点数n>=3,包含边 { v 1 , v 2 } , { v 2 , v 3 } , . . . , { v n − 1 , v n } \{v_1,v_2\},\{v_2,v_3\},...,\{v_{n-1},v_n\} {v1,v2},{v2,v3},...,{vn1,vn}
        请添加图片描述

      • W n W_n Wn:在轮 C n C_n Cn中心添加一个点,并与环上的点都相连
        请添加图片描述

      • 超立方体 Q n Q_n Qn 2 n 2^n 2n个点的超立方体图,每个点由长度为n的字符串表示,当两个点的字符串表示恰好只有1位不同时它们之间有边相连
        请添加图片描述

    • 平面图:在一个平面上,没有任何边(可以是直线或曲线)相交的图
    • 欧拉定理:一个平面简单图有 e 条边和 v 个顶点,r 是平面图分成的区域, r = e − v + 2 r = e - v + 2 r=ev+2
    • 一个平面简单图有 e 条边和 v 个顶点,v>=3 ⇒ \Rightarrow e<=3v-6,若没有长度为3的回路,则进一步 e<=2v-4
    • 一个平面简单图,任意顶点的度不超过5
    • 初等分割:移去图的边 { u , v } \{u,v\} {u,v},再添加顶点 w w w 和 边 { w , v } , { u , w } \{w,v\},\{u,w\} {w,v},{u,w} 得到一个新图。一个平面图通过初等分割仍然是平面图
    • 同胚图:从同一个图出发经过一系列初等分割得到的图
    • 图不是平面图    ⟺    \iff 包含 K 3 , 3 K_{3,3} K3,3 K 5 K_{5} K5 的同胚子图
    • 对偶图:一个图的每个区域对应一个点,若区域有公共边(只在一点相接的情况除去),则对应的的点相连
    • 简单图的着色:每个点分配一种颜色,相连的点颜色不同
    • 色数:完成简单图着色所需最少颜色数,用 χ ( G ) \chi(G) χ(G) 表示
    • 四色定理:平面图的色数不超过4
  • 从图的顶点 i 到 j 有长度为 r 的 A i j r A^{r}_{ij} Aijr 不同路径数,累乘邻接矩阵可以:
void DFS(int v){
    //O(N^2)
    //从起始点访问到v这个节点已经走了step步
    cout << v << ' ';
    visited[v]=true;
    for (int i=0;i<nV;i++){
        if (weightedG[v][i]!=(weightType)0 && !visited[i]){
        	step++;
        	DFS(i);
        	step--;
        }
    }
}
//☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
//☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
void BFS(int v){
    //O(N^2)
    queue<int> q;
    visited[v]=true;
    q.push(v);
    int last=v;  //第一层只有一个元素,自己就是队尾
    cout << v << ' ';
    while (!q.empty()){
        int w=q.front();q.pop(); //从起始点访问到w这个节点已经走了step步
        for (int i=0;i<nV;i++){
            if (weightedG[w][i]!=(weightType)0 && !visited[i]){
                cout << i << ' ';
                visited[i]=true;
                q.push(i);
            }
        }
    }
    if (w==last){
    	last=q.back();  //这一层已经全部访问,下一层已经全部入队,下一层的最后一个节点就是队尾的
    	step++;
    }
}
#include <iostream>
#include <queue>
#define MaxSize 100
using namespace std;


template<typename weightType>
class mEdge{
public:
    int v1,v2;
    weightType weight;
    mEdge(int _v1,int _v2,weightType _weight):v1(_v1),v2(_v2),weight(_weight){};
};


template<typename weightType,typename dataType>
class mGraph{
public:
    //邻接矩阵表示的有权图
    //图是一种多对多关系,是链表和树的扩展
    //(v,w)是无向边,<v,w>是有向边
    //图的数据包括非空有限顶点集和有限边集
    //{G_00,G_10,G_11,...,G_n-1n-1}可以省一半空间,Gij的下标是(i*(i+1)/2+j)
    //☆有权图如果没有边应该怎么表示?☆
    int nV,nE; //边数和顶点数
    weightType weightedG[MaxSize][MaxSize]={0};   //带权重的图
    dataType data[MaxSize]={0};                  //存每个顶点的数据
    bool visited[MaxSize]={false};             //遍历图需要
    mGraph(int vertexNum):nE(0),nV(vertexNum){};
    void InsertEdge(mEdge<weightType>* e){
        if (e->v1>=nV && e->v2>=nV){
            cout << "边的顶点违法" << endl;
            return;
        }
        weightedG[e->v1][e->v2]=e->weight;
        nE++;
    }
    void DFS(int v){
        //O(N^2)
        cout << v << ' ';
        visited[v]=true;
        for (int i=0;i<nV;i++){
            if (weightedG[v][i]!=(weightType)0 && !visited[i]) DFS(i);
        }
    }
    void reSetVisited(){
        //遍历结束之后需要重置visited方便下次遍历
        for (int i=0;i<nV;i++) visited[i]=false;
    }
    //☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
    //☆☆☆☆这里必须这么写,不能在while里cout w,会有重复☆☆☆☆
    //☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
    void BFS(int v){
        //O(N^2)
        queue<int> q;
        visited[v]=true;
        q.push(v);
        cout << v << ' ';
        while (!q.empty()){
            int w=q.front();q.pop();
            for (int i=0;i<nV;i++){
                if (weightedG[w][i]!=(weightType)0 && !visited[i]){
                    cout << i << ' ';
                    visited[i]=true;
                    q.push(i);
                }
            }
        }
    }
};

template<typename weightType>
class EdgeAndNode{          //□-
public:
    weightType weight;
    int vertexIndex;
    EdgeAndNode<weightType>* next;
    EdgeAndNode(weightType w,int i):weight(w),vertexIndex(i),next(nullptr){};
    EdgeAndNode(weightType w,int i,EdgeAndNode<weightType>* n):weight(w),vertexIndex(i),next(n){};
};

template<typename weightType>
class lEdge{
public:
    int v1,v2;
    weightType weight;
    lEdge(int _v1,int _v2,weightType w):v1(_v1),v2(_v2),weight(w){};
};

template<typename weightType,typename dataType>
class firstNode{            //□
public:
    dataType data;
    int vertexIndex;
    EdgeAndNode<weightType>* firstEdge;
    firstNode(dataType d,int i):data(d),vertexIndex(i),firstEdge(nullptr){};
};

template<typename weightType,typename dataType>
class lGraph{
public:
    int nV,nE;                                                  //边数和顶点数
    firstNode<weightType,dataType>* weightedG[MaxSize];         //带权重的图
    bool visited[MaxSize]={false};                                //遍历图需要
    lGraph(int vertexNum){
        nV=vertexNum;nE=0;
        for (int i=0;i<nV;i++) weightedG[i]=new firstNode<weightType,dataType>((dataType)-1,i);
    }
    lGraph(int vertexNum,dataType data[]){
        nV=vertexNum;nE=0;
        for (int i=0;i<nV;i++) weightedG[i]=new firstNode<weightType,dataType>(data[i],i);
    }
    void InsertEdge(lEdge<weightType>* e){
        int i;
        for (i=0;i<nV && weightedG[i]->vertexIndex!=e->v1;i++);
        if (i==nV){
            cout << "边的起点不存在" << endl;
            return;
        }
        EdgeAndNode<weightType>* tmp=new EdgeAndNode<weightType>(e->weight,e->v2,weightedG[i]->firstEdge);
        weightedG[i]->firstEdge=tmp;
    }
    void DFS(int v){
        visited[v]=true;
        cout << v << ' ';
        EdgeAndNode<weightType>* tmp=weightedG[v]->firstEdge;
        while (tmp){
            if (!visited[tmp->vertexIndex]) DFS(tmp->vertexIndex);
            tmp=tmp->next;
        }
    }
    void BFS(int v){
        //遍历当前节点的邻接节点并入队
        queue<int> q;
        q.push(v);
        cout << v << ' ';
        visited[v]=true;
        while (!q.empty()){
            int w=q.front();q.pop();
            EdgeAndNode<weightType>* tmp=weightedG[w]->firstEdge;
            while (tmp){
                if (!visited[tmp->vertexIndex]){
                    cout << tmp->vertexIndex << ' ';
                    visited[tmp->vertexIndex]=true;
                    q.push(tmp->vertexIndex);
                }
                tmp=tmp->next;
            }
        }
    }
};
int main(){
    int vertexNum,edgeNum;
    cin >> vertexNum;

    mGraph<int,int>* mg=new mGraph<int,int>(vertexNum);
    //lGraph<int,int>* lg=new lGraph<int,int>(vertexNum);

    cin >> edgeNum;
    for (int i=0;i<edgeNum;i++){
        int v1,v2,weight;
        cin >> v1 >> v2 >> weight;
        mEdge<int>* e=new mEdge<int>(v1,v2,weight);
        //lEdge<int>* e=new lEdge<int>(v1,v2,weight);
        mg->InsertEdge(e);
        //lg->InsertEdge(e);
    }
    mg->BFS(0);
    //lg->BFS(0);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_森罗万象

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

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

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

打赏作者

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

抵扣说明:

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

余额充值