数据结构—图(简要理解)

本文前半部分参考的是浙大MOOC上陈越老师说讲解,但听到后面一半,觉得有点晦涩难懂,便去看了《大话数据结构》,因此后面的主要内容也是大多参考这本书,除了最后一节的关键路径算法,有点搞不太懂,其他的总算让我有了浅显的了解了。说实话,数据结构实在是太难啃了,书本上讲的所以然,还得靠多刷题,多做实验来解决。

什么是图

  • 多对多
  • 线性表,树都是图的特殊情况
  • 包含:
    • 一组顶点,通常用V(Vertex)表示顶点集合
    • 一组边,通常用E(Edge)表示边的集合
    • 边是顶点对,(v,w),可以互通
    • 有向边,<v,w>,单向
    • 不考虑重边和自回路

抽象数据类型定义

类型名称,图

数据对象集,G(V,E),由一个非空的有限顶点集合v(顶点必须是要存在的)和一个有限集合E组成

操作集

  • Graph Create() 建立并返回空图
  • Graph InsertVertex(Graph G,Vertex v)将v插入G中
  • Graph InsertEdge(Graph G, Edge e)将边e插入G中
  • void DFS(Graph G,vertex V),从顶点v出发深度优先遍历图G
  • void BFS(Graph G,Vertex v),从顶点v出发宽度优先遍历图G
  • void shortestPath(Graph ,Vertex v, int Dist[]),计算图G中顶点V到任意其他顶点的最短距离
  • Void MST (Graph G),计算图G的最小生成树

常见术语

无向图,图中所有的边都是没有方向的

有向图,图中所有的边有可能是双向也可能是单边

权重,每个边具有数值

网络,带权重的图称为网络

邻接点,有边直接相连的顶点

度,跟这个顶点相关的所有顶点的个数

有向图:

  • 出度,从这个点发出的边数,对应行非0元素的个数
  • 入度,指向改点的边数,对应列非0元素的个数

在程序中表示一个图

邻接矩阵

邻接矩阵,用二维数组表示一个图,G[N] [N] ,若两个顶点存在直接连通的边,则G[N] [N]设置为1,否则,设置为0

问题,对于邻接矩阵存储,如何省下一半空间:

->只存下三角矩阵

  • 只管简单,容易理解
  • 方便检查任意一对顶点间是否存在边
  • 方柏霓找任意顶点的所有邻接点
  • 方便计算任意顶点的度

缺点:

  • 浪费空间,(存稀疏图,点很多,边很少,有大量无效元素)
  • 对稠密图(完全图)较合算
  • 浪费时间,统计稀疏图
邻接表

其为一个链表的集合

G[N] 为指针数组,对应矩阵每行一个链表,只存非0元素

邻接表的表示法是不唯一的,对于网络,结构中要增加权重的域,一定要够稀疏才合算

  • 方便找任意顶点的所有邻接点
  • 节约稀疏图的空间,需要N个头指针+2E个节点,每个节点至少两个域
  • 方便计算任意顶点的度,对无向图正确,对有向图,只能计算出度,入度的话吗,则需要构造逆邻接表,存指向自己的边

缺点

无法简便检查任意一对顶点间是否存在边

图的遍历

深度优先搜索(DFS)

先按照一条路走,若无全通则前进,若都已全通则原路返回,直至返回到起点

类似与树的先序遍历

若有N个顶点,E条边,时间复杂度为:

  • 邻接表存储图,O(N+E)

  • 邻接矩阵存储图,O(N*N)

    深度优先搜索(DFS)
    void DFS(vertex v)
    {
    	visited[V] = true;
    	for(v的每个邻接点w)
    		if(!visited(W))
    			DFS(w)
    }
    

广度优先搜索(BFS)

相当于树的层序遍历

若有N个顶点,E条边,时间复杂度为:

  • 邻接表存储图,O(N+E)
  • 邻接矩阵存储图,O(N*N)
void BFS(Vertex v)
{
	visited[v] = true;
	Enqueene(v, Q);//入队
	while (!Isempty(Q))
	{
		v = Dequeue(Q);//出队
		for (v的每个邻接点w)
		{
			if (!visited(w))
			{
				visited[w] = true;
				Enqueue(w,Q);
			}
		}
	}
}

DFS比较适合判断图中是否有环,寻找两个节点之间的路径,有向无环图(DAG)的拓扑排序,寻找所有强连通片(SCC),无向图中寻找割点和桥等;

BFS则比较适合判断二分图,以及用于实现寻找最小生成树(MST),如在BFS基础上的Kruskal算法。还有寻找最短路径问题(如Dijkstra算法)。

图不连通怎么半

连通:如果从V到w存在一条(无向)路径,则称v和w是连通的

路径:v到w的路径是一系列顶点{v,v1,v2,…,vn,w}的集合,其中任一对相邻的顶点间都有图中的边。

路径的长度:路径中的边数(如果带权,则是所有边的权重和)

简单路径:如果v到w之间的所有顶点都不同。

回路:起点等于终点的路径

连通图:图中任意两顶点均连通

连通分量:无向图的极大连通子图

  • 极大顶点数:再加1个顶点就不连通
  • 极大边数:包含子图中所有顶点相连的所有边

强连通:有向图中顶点v和w之间存在双向路径,则称v和w是强连通的

强连通图:有向图中任意两顶点均强连通

强连通分量:有向图的极大强连通子图

每调用一次DFS(V),就把v所在的连通分量遍历了一遍。BFS也是一样

//处理未连通的点
void LIstcomponents(Graph G)
{
	for (each V in G)
	{
		if (!visited[v])
		{
			DFS(V)//or BFS(V)
		}
	}

}

邻接矩阵表示图

#include<stdio.h>
#include<iostream>
#include <malloc.h>

using namespace std;
//图的邻接表,表示法

#define MaxVertexNum 100 //最大顶点数设置为100
typedef int  vertex;
typedef int weightype;
typedef char datatype;

//定义边

typedef struct ENode* ptrToENode;
struct ENode {
	vertex v1, v2; //有向边<v1,v2>
	weightype weight;
};
typedef ptrToENode Edge;

//邻接点定义
typedef struct AdjvNode* PtrToAdjvNode;
struct AdjvNode
{
	vertex adjv;//邻接点的小标
	weightype weight;//边权重
	PtrToAdjvNode Next;//指向下一个邻接点的指针
};

//顶点表头结点的定义
typedef struct Vnode
{
	PtrToAdjvNode FirstEdge;//边表头指针
	datatype Data;//存储点的数据
	//很多情况下,顶点无数据,此事Data可以不用出现
}Adjlist[MaxVertexNum];//Adjlist是邻接表类型

//定义图
typedef struct GNode* ptrToGNode;
struct GNode
{
	int Nv;//顶点数
	int Ne;//边数
	Adjlist G;
};
typedef ptrToGNode LGraph;//以邻接表存储的图类型


//初始化表
LGraph CreateGraph(int VertexNum)
{
	vertex v;
	LGraph Graph;

	Graph = (LGraph)malloc(sizeof(struct GNode));
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	//初始化邻接表头指针
	//默认顶点编号从0开始,到(Graph->NV-1)
	for (v = 0; v < Graph->Nv; v++)
	{
		Graph->G[v].FirstEdge = NULL;
	}
	return Graph;
}


void InsertEdge(LGraph Graph, Edge E)
{
	//插入边<v1,v2>
	//为v2建立新的邻结点
	PtrToAdjvNode NewNode;
	NewNode = (PtrToAdjvNode)malloc(sizeof(struct AdjvNode));
	NewNode->adjv = E->v2;
	NewNode->weight = E->weight;

	//将v2插入V1的表头
	NewNode->Next = Graph->G[E->v1].FirstEdge;
	Graph->G[E->v1].FirstEdge = NewNode;

	//如果是无向图,还要插入边<v2,v1>
	//为v1建立新的邻接点

	NewNode = (PtrToAdjvNode)malloc(sizeof(struct AdjvNode));
	NewNode->adjv = E->v1;
	NewNode->weight = E->weight;

	//将v1插入V2的表头
	NewNode->Next = Graph->G[E->v2].FirstEdge;
	Graph->G[E->v2].FirstEdge = NewNode;
}

LGraph BuildGraph()
{
	LGraph Graph;
	Edge E;
	vertex v;
	int Nv, i;
	cin >> Nv;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值