数据结构 - 图

数据结构 - 图,第1张

数据结构 - 图 1. 图的定义和术语
  1. 图的定义和术语
G = (V,E)
Graph = (vertex , Edge)
V : 顶点(数据元素)的有穷非空集合;
E : 边的有穷集合

  1. 完全图:
任意两个点都有一条边相连
无向完全图:
	有n个顶点
	n(n-1)/条边
有向完全图:
	有n个顶点
	n(n-1)条边
  1. 稀疏图
有很少边或者弧的图(e


  1. 稠密图
有很多边或者弧的图
  1. 网:
边/弧带权的图 
  1. 邻接
有边/弧相连的两个顶点之间的关系

()表示不分先后,vi 和vj 互为邻接点
  vi到vj 
  1. 顶点的度
与该点相关联的边的数目,记为TD(V)
有向图
ID: 入度
OD: 出度
度=ID + OD
  1. 路径
连续的边构成的顶点序列。


  1. 路径长度
路径上边或弧的数目/权值之和。


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


  1. 简单路径
除了起点和终点可以相同外,其余顶点均不相同的路径,途径两个相同的点,就是非简单路径

12.简单回路

除路径起点和重点相同外,其余顶点均不相同的路径。


起点和终点相同,途径两个相同的点,就是非简单回路

  1. 连通图
	在无(有)向图G={V,{E}}中,若任何两个顶点v、u 都存在从v到u的路径,则称G是连通图(强连通图)
  1. 权和网
	图中的边或者弧具有相关数称为权,表明从一个顶点到另一个顶点的距离或者耗费

15.连通分量

 	无向图G的极大连通子图称为G的连通分量。


  1. 强连通分量
	有向图G的极大强连通子图称G的强连通分量
  1. 图的逻辑结构(多对多)
	图没有顺序存储结构,但可以结组二位数组来表示元素空间关系
	链式存储结构:
		多重链表·
			邻接表
			邻接多重表
			十字链表
		数组:链接矩阵表示法
		链式: 邻接表
  1. 数组(邻接矩阵)表示法
	建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点的关系)

2. 图的存储结构 1. 邻接矩阵

· 1. 邻接矩阵

int MAXInt 极大值
int MAXNum 最大顶点数目
char  VerTexTyoe 顶点的数据类型
int ArcType 假设边的权值类型为整型


邻接表二点数据结构类型
struct
	VerTexType vexs[MAXnum]//顶点的数量
	ArcType arcs[MAXNum][MAXNum] //邻接矩阵
	int vexnum,arcnum;//图的当前的点数和变数
	
//AMGraph &G   
  1. 怎么创建一个邻接矩阵
//采用邻接矩阵表示法,创建无向图G
Status CreateUDN(AMGraph $G){
	//输入总的顶点数目,总边数
	cin>> G.vexnum>>G.arcnum;
	for(int = 0; G.vexnum; ++i)
		//依次输入边的信息
		cin>> G.vexs[i];
	//初始化邻接矩阵
	for (int i = 0; i>v1>>v2>>w;
		i=LocateVex(G,V1);
		j=LocateVex(G,V2);
		G.arcs[i][j] = w;
		G.arcs[j][i] = G.arcs[i][j];
	}
	return ok;

}
  • LocateVex是在顶点表中查找顶点的下标

​ a. 建立顶点表和邻接矩阵的关系

​ b. 表示顶点和边的关系

​ 设图A = (V,E)有个n个顶点,则顶点表如下:

i012n-1
Vexs[i]V1V2V3Vn

​ 邻接矩阵:

​ 比如设置V2 和 V3 存在边的关系或者权值w

​ 先去顶点表中查找V2的下标为 1,V3的下标的为2

​ 设置邻接矩阵

​ arcs[1][2] = 1或者w;

​ arcs[2][1] = 1 或者w;

012n-1
0
11/w
21/w
n-1

3.采用邻接矩阵表示法创建无向图总结:

1). 输入总顶点数和总边数

2). 依次输入点的信息存入顶点表中

3).初始化邻接矩阵,使每个权值初始化为极大值。


4).构造邻接矩阵。


2. 邻接表
  1. 邻接表表示法

  • 顶点:
    • 按照编号顺序将顶点数据存储在一维数组中
  • 关联同一个点的边(以顶点为尾的弧)
    • 用线性链表存储
  • 无向图和有向图的邻接表的区别:
1. 无向图的邻接表不唯一
2. 若无向图中有n个顶点、e条边,则邻接表需要n个结点和2e个表节点,适合存储稀疏图。


  1. 图的邻接表存储表示
  • 顶点的结构VNode
VerTexType(顶点的相关信息)firstarc(第一个边结点坐标)
 tpedef struct VNode{
	//顶点的信息
	VerTexType data;
	//指向第一条依附该顶点的边指针    
 	ArcNode * firstarc;
 }VNode,AdjList[MVNum];//定义数组类型AdList为VNode的数组类型
  • 边节点的定义和结构如下ArcNode:
adjvex(结点的下标位置)nextarc(下一个边结点的指向)info(边相关信息)
//最大顶点数目
#define MVN 100
//边节点结构
tyoedef struct ArcNode{
	//指向顶点的位置
	int adjvex;
	//指向下一条边的指针
	struct ArcNode *nextarc;
	//和边相关的信息
	OtgerInfo infol
}
  • 邻接表表示的图的结构
typedef struct{
	//node节点的列表
	AdjList vertices;
	//图的当前顶点数和弧数
	int vexnum,arcnum;
}ALGraph;
  • 设置图的相关参数
G.vexum = 5;G.arcnum = 5;
G.vertices[1].data='b';
p = G.vertices[1].firtarc;
p -> adjvex = 4;
  • 怎么建立邻接表
    • 算法思想如下
1. 输入总的顶点数和总边数
2. 建立顶点表
	依次输入点的信息存入顶点表中
	使每个表头结点的指针域初始化为null
3. 创建邻接表
	依次输入每条边依附的两个顶点
	确定两个顶点的序列号i和j,建立边结点
	将此结点分别插入vi和vj对应的两个边链表的头部
  • ​ 采用邻接表创建无向网
 Status  CreateUDG(ALGraph &G){
 	//输入总的顶点数,总边数
 	cin>>G.vexnum>>G.arcnum;
 	//输入各点,构造表头结点表
 	for(int i = 0; i< G.vexnum;++i){
 		//输入顶点值
 		cin>> G.vertices[i].data;
 		//初始化表头结点的指针域
 		G.vertices[i].firstarc = NULL;
 	}
 	for(k= 0; G.arcnum; ++k){
 		//输入边的关系
 		cin>>v1>>v2;
 		//查找v1,v2在数组的中位置
 		i = LocateVex(G,v1);
 		j = LocateVex(G,v2);
 		p1 = new ArcNode;
 		p1-> adjvex = j;
 		p1-> nextarc = G.vertices[i].firstarc;
 		G.vertices[i].firstarc = p1;
        //由于是无向的j的边也要指向这条边
 		p2 = new ArcNode;
 		p2->adjvex =i;
 		p2->nextarc = G.vertices[j].firstarc;
 		G.vertices[j].firstarc = p2;
 	}
 	return ok;
 }
  • 有向图和无向图的邻接表优缺点
	有向图: 
		缺点:求结点的度困难
	无向图:
		缺点:每条边都要存储两边
3. 十字链表
  • 顶点结点
datafirstin(入度边)firstout(出度边)
  • 弧结点
tailvex(弧尾结点坐标)headvex(弧头结点坐标)hlink(下一个弧头相同的弧)tlink(下一个弧尾相同的弧)
  • 示例

4.邻接多重表
  1. 解决的问题
	解决了无向图的每个边都要存储俩遍的问题
markivexinlinkjvexjlinkinfo
5. 图的遍历
  1. 遍历的定义:
	从已经给的连通图的某一个顶点出发,沿着一些边访问图的所有的顶点,且使每个顶点仅被访问一次
  1. 图的特点:
	图中可能存在的回路
  • 怎么避免重复访问?
	解决思路:设置辅助数组visited[n],用来标记每个被访问过的顶点。


初始化状态visited[i] 为 0; 顶点i被访问,改visited[i]为1,防止多次被访问

  1. 图常用的遍历方法:
  • 深度优先遍历-DFS
  • 广度优先遍历-BFS
  1. 深度优先算法的实现

  • 邻接矩阵
123456
1011100
2100010
3100010
4100001
5011000
6000100
  • 采用邻接矩阵表示连通图的优先遍历
void DFS(AMGraph G,int v){
	//访问第V个顶点
	cout<
  • 邻接矩阵的时间时间复杂度O(n^2)

  • 邻接表的的时间复杂度O(n个头结点+e)

  • 非连通图的遍历

    • 连通图已经访问过的结点后
    • 从未访问的结点的结点中获取开始结点进行遍历
  1. 广度优先遍历
1. v1 插入队列
2. v1 出队
3. 将v1的邻接点入队
....
  • visited[i]
0123

  • 广度优先的算法实现
	void BFS(Graph G,int v){
		cout<=0;w = NextAdjVex(G,u,w)){
				//尚未访问的邻接顶点的结点入队
				if(!visited[w]){
					cout<< w;
					visited[w] = true;
					EnQueue(Q,w);
				}
			}
		}
	}
3. 图的应用
  1. 最小生成树
	最小生成树:给定一个无向网,在该网的所有生成树中,使的各个边权值之和最小的那棵树称为最小生成树,最小代价生成树
  • 构造最小生成树Minimum Spanning Tree
	构造最小生成树的算法很多,利用MST性质
	MST:
		设 N = (V,E) 是一个连通网,U 是顶点集 V 的一个非空子集。


若边(u,v) 是一条具有最小权值的边,其中 u∈U, v∈V-U,则必存在一棵树包含(u,v)的最小生成树

在生成树的构造过程中,图中n个顶点分属两个集合:
	1.已经落到生成树上的顶点集: U
	2.尚未落到生成树上的顶点集: V - U
接下来则应在所有连通U中顶点和V - U 中顶点的边中选取权值最小的边
  • Prim 算法
 1.设N= (V,E)是连通网,TE是N上最小生成树中的边的集合。


2.初始令 U = {u0},(u0∈V),TE={}。


3.在所有u∈U,v∈V-U的边(u,v)∈E中,找一条代价最小的边(u0,v0).

  • 克鲁斯卡尔(KrusKal)算法
  1. 最短路径

  • Dijistra算法
1. 初始化: 先找出从源点V0到各终点的vk的直达路径(v0,vk),即通过一条弧到达的路径
2. 选择: 从这些路径中找出一条长度最短的路径(v0,u)
3. 更新:然后对其余各条路径进行调整:
	若在图中存在弧(u,vk),且(v0,u)+(u,vk)<(v0,vk)
	则以路径(v0,u,vk)代替(v0,vk)。


V - U
接下来则应在所有连通U中顶点和V - U 中顶点的边中选取权值最小的边


* Prim 算法

1.设N= (V,E)是连通网,TE是N上最小生成树中的边的集合。



2.初始令 U = {u0},(u0∈V),TE={}。



3.在所有u∈U,v∈V-U的边(u,v)∈E中,找一条代价最小的边(u0,v0).


[外链图片转存中...(img-e6Vytsap-1649150426788)]

* 克鲁斯卡尔(KrusKal)算法

[外链图片转存中...(img-ouGGtuXe-1649150426789)]

2. 最短路径

[外链图片转存中...(img-Twk37u3P-1649150426789)]

[外链图片转存中...(img-v482b8tS-1649150426789)]

* Dijistra算法

  1. 初始化: 先找出从源点V0到各终点的vk的直达路径(v0,vk),即通过一条弧到达的路径
  2. 选择: 从这些路径中找出一条长度最短的路径(v0,u)
  3. 更新:然后对其余各条路径进行调整:
    若在图中存在弧(u,vk),且(v0,u)+(u,vk)<(v0,vk)
    则以路径(v0,u,vk)代替(v0,vk)。


4. 图的遍历Java实现
  1. DFS-Java代码实现如下
package com.lj.advance;

import java.util.Scanner;

public class DFS {
    static final int MAXINT = Integer.MAX_VALUE;
    static final  int MAX_NUM = 233;

    static int[] visited  = new int[MAX_NUM];
    static class AMGraph {
        //顶点集合
        String[] vexes = new String[MAX_NUM];
        //邻接矩阵
        int[][]  arcs = new int[MAX_NUM][MAX_NUM];
        //顶点数
        int vexNum;
        //边数
        int arcNum;
    }

    /**
     * 初始化邻接矩阵
     * @param G
     * @return
     */
    static   boolean createUDN(AMGraph G){
        Scanner sc = new Scanner(System.in);
        int vexNum = sc.nextInt();
        int arcNum = sc.nextInt();
        G.vexNum  = vexNum;
        G.arcNum = arcNum;
        sc =  new Scanner(System.in);
        for (int i= 0 ;i
  1. BFS-代码实现如下
package com.lj.advance;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class BFS {
    public static final int MAX_NUM = 100;

    public static int[] visited = new int[MAX_NUM];

    /**
     * 顶点结构
     */
    static class VNode {
        //顶点的数据信息
        String data;
        //边结构依赖坐标指向
        ArcNode firstArc;
    }

    /**
     * 边结构
     */
    static class ArcNode {
        //指向的顶点位置
        int adjVex;
        //指向下一条边的指针
        ArcNode nextArc;
    }


    /**
     * 设置图的结构
     */
    static class ArcGraph {
        //顶点的列表
        VNode[] vertices = new VNode[MAX_NUM];
        //图的当前顶点数
        int vexNum;
        //图的当前的边数
        int arcNum;
    }

    static boolean createUDG(ArcGraph G) {
        Scanner sc = new Scanner(System.in);
        //设置图的顶点数目
        G.vexNum = sc.nextInt();
        //设置图的边数
        G.arcNum = sc.nextInt();
        sc = new Scanner(System.in);
        //构造顶点列表结构
        for (int i = 0; i < G.vexNum; i++) {
            VNode vNode = new VNode();
            vNode.data = sc.next();
            vNode.firstArc = null;
            G.vertices[i] = vNode;
        }
        //构造边结构
        for (int k = 0; k < G.arcNum; k++) {
            //输入边的关系
            sc = new Scanner(System.in);
            String v1 = sc.next();
            String v2 = sc.next();
            //点位边的下标索引
            int i = LocateVex(G, v1);
            int j = LocateVex(G, v2);
            ArcNode nodeI = new ArcNode();
            nodeI.adjVex = j;
            nodeI.nextArc = G.vertices[i].firstArc;
            G.vertices[i].firstArc = nodeI;
            ArcNode nodeJ = new ArcNode();
            nodeJ.adjVex = i;
            nodeJ.nextArc = G.vertices[j].firstArc;
            G.vertices[j].firstArc = nodeJ;
        }
        return true;
    }

    static void bfs(ArcGraph G, int v) {
        System.out.println("访问第" + v + "顶点");
        visited[v] = 1;
        Queue queue = new LinkedList<>();
        //将该顶点入队列
        queue.add(v);
        while (!queue.isEmpty()) {
            Integer index = queue.poll();
            for (int w = firstAdjVex(G,index);w>=0;w = nextAdjVex(G,index,w)){
                if(visited[w]==0){
                    System.out.println("访问第" + w + "顶点");
                    visited[w] = 1;
                    queue.add(w);
                }
            }
        }
    }

    static int nextAdjVex(ArcGraph G,int u, int index){
        ArcNode p = G.vertices[u].firstArc;
        ArcNode node = null;
        while (p!=null){
            int adjVex = p.adjVex;
            if(adjVex==index){
                node = p;
            }
            p = p.nextArc;
        }
        if(node!=null&&node.nextArc!=null){
           return node.nextArc.adjVex;
        }else {
            return -1;
        }
    }

    static int firstAdjVex(ArcGraph G, int u) {
        ArcNode firstArc = G.vertices[u].firstArc;
        if (firstArc != null) {
            return firstArc.adjVex;
        } else {
            return -1;
        }
    }

    static int LocateVex(ArcGraph G, String v) {
        for (int i = 0; i < G.vexNum; i++) {
            String data = G.vertices[i].data;
            if (v.equals(data)) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        ArcGraph graph = new ArcGraph();
        createUDG(graph);
        VNode[] vertices = graph.vertices;
        for (int i = 0; i < graph.vexNum; i++) {
            System.out.print("vex" + vertices[i].data + ":" + "\t");
            ArcNode p = vertices[i].firstArc;
            while (p != null) {
                System.out.print("next adj " + p.adjVex + "\t");
                p = p.nextArc;
            }
            System.out.println();
        }

        for (int i = 0;i

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/564800.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-06
下一篇 2022-04-06

发表评论

登录后才能评论

评论列表(0条)