数据结构与算法笔记——图篇

本文详细介绍了图的基本概念,包括图的定义、生成子图和路径、图的分类(有向图、无向图、简单图与多重图等),以及常用的图存储结构(邻接矩阵、邻接表等)。此外,还涵盖了深度优先搜索和广度优先搜索算法,以及图在最小生成树和最短路径问题中的应用,如Kruskal和Prim算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 

目录

前言

一、图相关的基本概念

1.图的定义

2.生成子图与路径

3.图的分类

 (1)有向图与无向图

 (2)简单图与多重图

 (3)有向与无向完全图

 (4)稠密图和稀疏图(边少与边多的图)

二、图的存储结构

1.邻接矩阵法

2.邻接表法

3.邻接多重表法

4.十字链表法

三、图的搜索

1.深度优先搜索DFS

2.广度优先搜索BFS

四、图的应用

1.最小生成树

         (1)Kruskal算法

(2)Prim算法

2.最短路径

总结


 


前言

个人关于图的基本概念梳理整合


 

一、图相关的基本概念

1.图的定义

图G由顶点集V和边集E组成,记为 G = (V , E);

V(G) 表示图G中顶点的有限非空集这与树、线性表可为空集不同);

E(G)表示图G中顶点之间的关系集合。

2.生成子图与路径

生成子图定义:

包含原图G中所有顶点的子图。

路径:

起始顶点到终点顶点的顶点序列。(若是不包含重复的顶点的路径,则称为简单路径)

路径的长度:

路径上边的数目,若该路径最短,则称其为距离。

回路:

第一个顶点和最后一个顶点相同的路径。

 

3.图的分类

 (1)有向图与无向图

无向图

        (1)连通图:任意两个顶点之间都是连通的,即两顶点间都存在路径,可以互相往来

        (2) 极大连通子图(连通分量):在保持连通的情况下,尽可能多的包含顶点和边的子图。(若原图为连通图,则连通分量就是其本身;若原图为非连通图时,则连通分量有多个)

        (3)极小连通子图:在某一顶点子集所确定的连通子图中包含的边最少的连通子图。(若有N个顶点,则有N-1条边,极小连通子图个数不一定唯一)

        (4)生成树:由原图的全部顶点所确定的一个极小连通子图。(包含生成子图和极小连通子图的概念统一,原图必须为连通图,生成树的个数不一定唯一)

        (5)生成森林:由原非连通图的每个连通分量的生成树组成的非连通图。

        (6)顶点的度:以该顶点为端点的边的个数       

有向图

        (1)强连通图:任意两个顶点之间都是强连通的,即两顶点间都有相互达到的弧。

        (2)极大强连通子图:概念类似无向图

        (3)极小强连通子图:概念类似无向图

        (4)顶点的出度(或入度):以该顶点为起点(或终点)的弧的个数

        (5)顶点的度:该顶点的出度和入度的和

        (6)有向树:一个顶点的入度为0、其余顶点的入度均为1的有向图。(与树的区别,结点的度表示含义不同,树的结点度表示该结点的孩子结点的个数,而有向图的顶点的度表示该顶点的入度和出度之和)

 (2)简单图与多重图

        简单图:无重复边且不存在顶点到自身的边

        多重图:有重复边或存在顶点到自身的边

 (3)有向与无向完全图

       无向完全图:任意两个顶点之间都有边

       有向完全图:任意两个顶点之间都存在方向相反的弧

       注意:这与连通图顶点间要求有路径是不同的

 (4)稠密图和稀疏图(边少与边多的图)

二、图的存储结构

1.邻接矩阵法

实现:用二维数组来表示边的关系

#include<bits/stdc++.h>

using namespace std;

struct Vex{
    int num;
    string name;
};
typedef struct Graph{
    Vex vex[20];
    int edges[20][20];
}ALGraph;

2.邻接表法

用数组和链表相结合表示边的关系,用链表来存储图中边或弧的信息,也可以用动态数组(如vector)代替链表。存储效率优于邻接矩阵。

#include<bits/stdc++.h>

using namespace std;
//------------------------------------------------
1.基本写法

//边表
typedef struct ArcNode{
    int num;
    struct ArcNode *next;
}ArcNode;

//顶点表
typedef struct VNode{
    int data;
    string name;
    ArcNode *frist;
}VNode,AdjList[20];

//邻接表
typedef struct{
    AdjList vetices;
    int vexnum,arcnum;
}ALGraph;

2.使用vector代替链表,进行边的存储
vector<int> edge[500];

void add(int from, int to){
    edge[from].push_back(to);
}

3.用结构体代替链表,进行边的存储
struct Edge{
    int to, next;
}edge[V];

int head[N], tot;//tot表示下一个可用边

void init(){
    memset(head, oxff, sizeof(head));  //head初始化-1
    tot = 0;     //当前可用边为0
}

void addedge(int from, int to){
    edge[tot] = {to, head[from]};
    head[from] = tot++;
}


3.邻接多重表法

4.十字链表法

三、图的搜索

1.深度优先搜索DFS

#include<bits/stdc++.h>
#include<queue>
#define MAX 100

struct Vex{
    int num;
    string name;
};

struct Edge{
    int from,to,value;
};

class Graph{
private:
    Vex vex[MAX];
    int AdjMatrix[MAX][MAX];
    bool visited[MAX];
    int vexNum;
    queue<Vex> q;
public:
    void Init();
    bool DFS(int num);
    bool BFS(int num);
};

void Graph:: Init(){
    for(int i = 0; i < MAX; i++)
        visited[i] = false;
}

bool Graph::DFS(int num){

    visited[num] = true;
    cont << vex[num].num << endl;

    for(int j=0; j< MAX;j++)
        if(visited[j]!= true && AdjMatrix[num][j] == 1)
                DFS(j);
}

bool Graph::BFS(int num){

    visited[num] = true;
    q.push(vex[num]);

    while(!q.empty()){
        int n = q.size();
        while(n--){
            Vex vex = q.front();
            q.pop();
            visited[vex] = true;
            for(int j=0; j< MAX;j++)
                if(visited[j]!= true && AdjMatrix[num][j] == 1)
                        q.push(vex[j]);
        }
    }

}




2.广度优先搜索BFS

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     *
     * @param root TreeNodeÀà
     * @return intÕûÐÍvector<vector<>>
     */
    vector<vector<int> > levelOrder(TreeNode* root) {
        // write code here
        vector<vector<int>> res;
        if(!root )
            return res;
        queue<TreeNode *> qu;
        qu.push(root);
        while(!qu.empty()){
            vector<int> vec;
            int size = qu.size(); //统计每层个数
            while(size--){
            TreeNode *node = qu.front();
            qu.pop();
            vec.push_back(node->val);
            if(node->left)
                qu.push(node->left);
            if(node->right)
                qu.push(node->right);
            }
            if(vec.size() > 0)
               res.push_back(vec);
        }
           return res;
    }
};

四、图的应用

memset初始化:

const int N = 2e5 + 7;
int a[N][N];

memset初始化数组,是以第二个参数的转换为二进制后,最后面的8位来初始化所有内存的

memset(a, 0, sizeof(a));
memset(a, -1, sizeof(a));  等价于 memset(a, 0xff, sizeof(a));  //初始化-1

//初始化为无穷
#define INF 0x3f3f3f3f

memset(a, INF, sizeof(a));

1.最小生成树

(1)Kruskal算法

此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
1. 把图中的所有边按代价从小到大排序; 
2. 把图中的n个顶点看成独立的n棵树组成的森林; 
3. 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

(2)Prim算法

此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。

  1. 图的所有顶点集合为VV;初始令集合u={s},v=V−uu={s},v=V−u;
  2. 在两个集合u,vu,v能够组成的边中,选择一条代价最小的边(u0,v0)(u0,v0),加入到最小生成树中,并把v0v0并入到集合u中。
  3. 重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。

由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息,:

 

2.最短路径

 

 


总结

简单介绍了图的基本概念,需要重要理解的是两个顶点之间有与有路径区别、无向图的连通分量生成树的概念、有向图的顶点的度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值