文章目录

  • reference
  • terminology
  • 图的存储结构
    • 顺序存储
      • 邻接矩阵
        • code
      • 关联矩阵
    • 链式存储
      • 邻接表
      • 逆邻接表
      • 十字链表
      • 邻接多重表
  • 图的遍历
    • dfs
      • 思路
      • code
    • bfs
      • 思路
      • code
  • 最小生成树
    • prim算法
      • 思路
      • code
    • Kruskal算法
      • 思路

reference

link
(《大话数据结构》真是本好书

terminology

  • 顶点
  • 弧头
  • 弧尾
  • 有向图
  • 无向图
  • 边 无向图中两条弧可以用一条边来表示
  • 完全图 有n*(n - 1)/2的无向图
  • 有向完全图 有n*(n - 1)的有向图
  • 稀疏图
  • 稠密图
  • 网 带权值的图又称为网或网络
  • 子图 由图的部分顶点和边组成的新图称为原图的子图
  • 生成子图 由图的全部顶点和部分边组成的子图称为原图的生成子图
  • 邻接点 互为邻接点
  • 依附 边依附于顶点
  • 相关联 边与顶点相关联
  • 顶点的度
  • 顶点的入度
  • 顶点的出度
  • 路径 由顶点vi到顶点vj的顶点序列
  • 回路 起点和终点为同一顶点的路径
  • 连通图 无向图中两顶点之间有路径存在,则称为连通图
  • 连通分量 无向图中极大连通子图称为原图的连通分量
  • 强连通图 有向图中任意两顶点之间存在路径,则称该图为强连通图
  • 强连通分量 有向图中极大强连通子图称为原图的强连通分量
  • 生成树 连通图的极小连通生成子图称为原图的生成树

图的存储结构

顺序存储

邻接矩阵

图_第1张图片

code

typedef char VertexType;//顶点处的数据类型为字符
typedef int EdgeType;//边上的数据类型 
typedef struct{
	VertexType vexs[MAX];//顶点表 
	EdgeType arc[MAX][MAX];//邻接矩阵
	int vertexes, edges;//顶点数,边数
}MGraph;
void CreateGraph(MGraph *G)
{
	int i, j;//图的顶点和边数 
	int s, e, w;//边的起始点,终点,权值 
	cout<<"请输入图的顶点数和边数"<<endl;
	cin>>G->vertexes>>G->edges;//输入顶点数和边数
	cout<<"请输入图的所有顶点"<<endl; 
	for(i = 0; i < G->vertexes; i++)
	{
		cin>>G->vexs[i];	
	}
	//邻接矩阵初始化
	for(i = 0; i < G->vertexes; i++)
	{
		for(j = 0; j < G->vertexes; j++)
		{
			G->arc[i][j] = 0;		
		}
	} 
	cout<<"请输入图的所有边"<<endl; 
	for(j = 0; j < G->edges; j++)
	{
		cin>>s>>e>>w;//输入边的起始点,终点,权值
		G->arc[s - 1][e - 1] = w; 
	}
}

关联矩阵

图_第2张图片

链式存储

邻接表

图_第3张图片

逆邻接表

图_第4张图片

十字链表

邻接多重表

图的遍历

dfs

思路

1.对所有没有遍历过的节点dfs
2.dfs每一步,要做的是:
访问当前顶点(或者对当前顶点进行操作)
对当前顶点的所有相邻的没访问过的顶点dfs

code

void DFSTraverse(MGraph G)//深度优先遍历图 
{
	int i;
	//初始化
	for(i = 0; i < G.vertexes; i++)
	{
		visited[i] = false;
	}
	for(i = 0; i < G.vertexes; i++)
	{
		//如果该点没有遍历过,则深度优先遍历该点 
		if(!visited[i])
		{
			DFS(G, i);
		}
	}
}
void DFS(MGraph G, int i)
{
	int j; 
	visited[i] = true;//该顶点访问过了
	cout<<G.vexs[i];
	//访问该顶点的相邻顶点 
	for(j = 0; j < G.vertexes; j++)
	{
		//找到相邻的没有访问过的顶点并dfs 
		if(G.arc[i][j] != 0 && !visited[j])
		{
			DFS(G, j);
		}	
	} 
}

bfs

思路

用队列
从某一顶点出发。该顶点入队。
因为不需要栈,所以不用递归,只需要while循环即可。条件是队非空
每次要做的操作是,将队头顶点出队,访问,标志其为访问过,将其相邻顶点入队。

code

void BFS(MGraph G, int i)
{
	int a;
	int j;  
	q.push(i);//将第i个顶点入队 ,i为顶点标号 
	while(!q.empty())//当队列不为空的时候
	{
		a = q.front();//队头顶点出队
		cout<<"出队顶点:"<<a<<endl;
		q.pop();
		visited[a - 1] = true;//标记其为访问过
		cout<<G.vexs[a - 1];
		//将其相邻顶点入队 
		for(j = 0; j < G.vertexes; j++)
		{
			//找到相邻的没有访问过的顶点并入队 
			if(G.arc[a - 1][j] != 0 && !visited[j])
			{
				cout<<"入队顶点:"<<j + 1<<endl;
				q.push(j + 1);//将相邻顶点入队 
			}	
		} 	
	} 
}

刚开始写的代码是这样的,发现问题是,会重复遍历。问题在于:每次将其输出的时候才标记为访问过。这样的话,在还没有输出但是已经压队时,标记的是未访问过,这样就可能重复压队,重复输出。
修改的话,就是一旦压队就标记为已经访问过即可

void BFSTraverse(MGraph G)
{
	int i;
	//初始化
	for(i = 0; i < G.vertexes; i++)
	{
		visited[i] = false;
	}
	for(i = 0; i < G.vertexes; i++)
	{
		//如果该点没有遍历过,则广度优先遍历该点 
		if(!visited[i])
		{
			BFS(G, i+1);//从第i个顶点开始bfs 
		}
	}
	cout<<endl;
}
void BFS(MGraph G, int i)
{
	int a;
	int j;  
	q.push(i);//将第i个顶点入队 ,i为顶点标号 
	visited[i - 1] = true;//一旦入队就标记为访问过 
	while(!q.empty())//当队列不为空的时候
	{
		a = q.front();//队头顶点出队
		q.pop();
		cout<<G.vexs[a - 1];
		//将其相邻顶点入队 
		for(j = 0; j < G.vertexes; j++)
		{
			//找到相邻的没有访问过的顶点并入队 
			if(G.arc[a - 1][j] != 0 && !visited[j])
			{
				q.push(j + 1);//将相邻顶点入队 
				visited[j] = true;//一旦入队就标记为访问过 
			}	
		} 	
	} 
}

最小生成树

prim算法

思路

1.将图中顶点分为两个集合X,Y
2.将跨界这两个集合的权值最小的边加入图中,并将其依附在Y中的顶点加入到X中
3.反复过程2,知道Y为空,所得子图即为最小生成树

code

Kruskal算法

思路

1.将图中所有边按权值从小到大排序
2.取权值最小的边加入图中,判断是否形成回路。若没有,则保留;否则去掉该边。
3.反复过程2,直到所有顶点均连通为止

你可能感兴趣的:(数据结构)