zoukankan      html  css  js  c++  java
  • 图论基础

    基本概念

    1.图也是一种数据结构:是某类具体事物以及这些事物之间的联系。

    2.图:顶点(vertex)和(edge)组成。
    顶点:具体事物。
    边:具体事物之间的联系。

    顶点的集合V,边的集合E,所以图记为G = (V,E)。

    比如下图就是一个典型的图。。
    在这里插入图片描述


    图的分类

    0.带权图

    定义:边上带有权值的图。(不同问题中,权值意义不同,可以是距离、时间、价格、颜值 ……)


    1.无向图

    定义:边没有指定方向的图

    相邻:两个顶点之间如果有边连接,那么就视为两个顶点相邻。
    路径:相邻顶点的序列。
    :起点和终点重合的路径。
    连通图:任意两点之间都有路径连接的图。
    :顶点连接的边数叫做这个顶点的度。
    :没有圈的连通图。
    森林:没有圈的非连通图


    2.有向图

    定义:边具有指定方向的图(有向图中的边又称为弧,起点称为弧头,终点称为弧尾

    有向路径:相邻顶点的序列。
    有向环:一条至少含有一条边且起点和终点相同的有向路径。
    有向无环图(DAG):没有环的有向图。

    :一个顶点的入度与出度之和称为该顶点的度。
    1)入度:以顶点为弧尾的边的数目称为该顶点的出度
    2)出度:以顶点为弧头的边的数目称为该顶点的入度


    图的存储方式

    1.邻接矩阵

    E[u][v] 表示的是顶点u与顶点v的关系。

    如果顶点u和顶点v之间有边相连,

    E[u][v] = 1;
    

    否则

    E[u][v] = 0;
    

    如果是有权边,

    E[u][v] = cost;
    

    对于无向图

    E[u][v] = E[v][u];
    

    优劣性:

    优点:可以在常数时间内判断两点之间是否有边存在。
    缺点:表示稀疏图时,浪费大量内存空间。
    
    2.邻接表

    通过把“从顶点0出发有到顶点2,3,5的边”这样的信息保存在vector中来表示图

    邻接表非常的常用,所以我会在例题一解释它的具体用法。(当然所有的例题我都会使用邻接表进行存图


    图的遍历方式

    遍历类似于树的先根遍历,是树的先根遍历的推广。

    定义:假设初始状态是图中所有顶点未曾被访问,则深度优先搜索可从图中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。


    T1 有向图的 dfs
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 205;
    vector<int> E[MAXN];
    // 定义邻接表(edge)E
    bool vis[MAXN];
    // 记录是否访问过
    void Addedge(int u, int v) {
    // 存储边的信息
    	E[u].push_back(v); // 表示以u为起点的边中有一条边的终点是v
    }
    
    void dfs(int i) {
        printf("%d ", i); 
        vis[i] = true;
        sort(E[i].begin(), E[i].end());
        // 因为要求字典序所以需要进行排序
        for (int j = 0; j < E[i].size(); j++) {
        // 枚举边
            if (vis[E[i][j]] == false) {
            	dfs(E[i][j]); // 继续进行dfs
    		}
        }
    }
    
    int main() {
    	int m, n;	
        scanf("%d %d", &n, &m);
        for (int i = 1; i <= m; i++) {
        	int u, v;
            scanf("%d %d", &u, &v);
            E[u].push_back(v);
        }
        for(int i = 1; i <= n; i++) // 有可能不是连通图哦
        	if(vis[i] == false) // i没被访问过就dfs一次
        		dfs(i);
        return 0;
    }
    

    无向图同理。。。


    遍历类似于树的按层次遍历的过程。

    定义:假设从图中某顶点v出发,在访问v之后依次访问v的各个未被访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以v为起始点,由近至远,依次访问和v有路径相通且路径长度为1,2,…的顶点。


    T2 有向图的 bfs
    #include <cstdio>
    #include <queue>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 5005;
    vector<int> E[MAXN]; 
    // 邻接表
    bool vis[MAXN]; 
    // 记录是否被访问过
    void Addedge(int u, int v) {
    	// 存图
    	E[u].push_back(v);
    }
    
    void BFS(int st) {
    	queue<int> q;
    	q.push(st);
    	// 初始节点进队
    	vis[st] = true;
    	printf("%d ", st);
    	while(!q.empty()) { // 如果队列里还有节点
    		int t = q.front();
    		// 拿出第一个
    		q.pop();
        	sort(E[t].begin(), E[t].end()); 
        	// 因为要求字典序所以需要进行排序
    		for(int i = 0; i < E[t].size(); i++) {
    		// 枚举边
    			if(vis[E[t][i]] == false) {
    				vis[E[t][i]] = true;
    				printf("%d ", E[t][i]);
    				q.push(E[t][i]);
    				// 终点进队
    			}
    		}
    	}
    }
    
    int main() {
    	int n, m;
    	scanf ("%d %d", &n, &m);
    	for(int i = 1; i <= m; i++) {
    		int u, v;
    		scanf ("%d %d", &u, &v);
    		Addedge(u, v);
    	}
    	for(int i = 1; i <= n; i++) // 有可能不是连通图哦
    		if(vis[i] == false) // i没被访问过就dfs一次
    			BFS(i);
    	return 0;
    }
    

    无向图同理。。。

    突破极限,一旦放弃了就意味着结束,说不定身体里还隐藏着连自己都没有察觉到的力量,不要被所谓的极限所禁锢。

  • 相关阅读:
    c++的输入流基础知识
    用英文加优先级来解读C的声明
    django 用imagefiled访问图片
    关于Django中的表单验证
    c#语言的一些复习
    IIS发布的网站用localhost可以访问,改成IP就无法访问的解决方案 .
    首次关于IIS配置遇到的一些问题
    常见dos操作
    vs2012中对于entity framework的使用
    几个知识点
  • 原文地址:https://www.cnblogs.com/Chain-Forward-Star/p/13868185.html
Copyright © 2011-2022 走看看