数据结构与算法——有向无环图的拓扑排序C++实现
2016-04-29 12:42
603 查看
拓扑排序简介:
拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从Vi到Vj的路径,那么在排序中Vi在Vj的前面。
如果图中含有回路,那么拓扑排序是不可能的。此外,拓扑排序不必是唯一的,任何合理的排序都可以。
对于上面的无环图:v1,v2,v5,v4,v3,v7,v6和v1,v2,v5,v4,v7,v3,v6都是合理的拓扑排序。
一个简单的求拓扑排序的思路:
1、先找出任意一个没有入边的顶点2、然后显出该点,并将它和它邻接的所有的边全部删除。
3、然后,对图中其它部分做同样的处理。
图用邻接表表示法来存储:
参考博客:数据结构与算法——图的邻接表表示法类的C++实现
左边的数组此时就有用了,用来保存每个顶点的信息,该数组中每个元素的数据结构为:
//保存每个顶点信息的数据结构 struct GraphNode{ int vertex;//当前顶点的标号 int inDegree;//当前顶点的入度 int topNum;//当前顶点的拓扑排序的顺序标号 };
图的邻接表示法的类的接口:
/******************************************************* * 类名称: 邻接表图 ********************************************************/ class Graph{ private: int edge_num;//图边的个数 int vertex_num;//图的顶点数目 list<Node> * graph_list;//邻接表 vector<GraphNode> nodeArr;//保存每个顶点信息的数组 public: Graph(){} Graph(char* graph[], int edgenum); ~Graph(); void print(); vector<int> topoSort();//拓扑排序 private: vector<int> get_graph_value(char* graph[], int columns); void addEdge(char* graph[], int columns); };
拓扑排序成员函数:
/************************************************* * 函数名称:topoSort() * 功能描述:对图中的顶点进行拓扑排序 * 参数列表:无 * 返回结果:返回顶点拓扑排序之后的结果 *************************************************/ vector<int> Graph::topoSort() { vector<int> topoSortArr; for(int count = 0; count < vertex_num; ++count){ //找到一个入度为0的顶点 int i; for(i = 0; i < vertex_num; ++i){ if((nodeArr[i].inDegree == 0)&&(nodeArr[i].vertex != -1)) break; } if(i == vertex_num) break; //此时顶点i的入度为0 //删除该点和删除与该点相邻的边 //并将与顶点i相连的顶点的入度减1 nodeArr[i].inDegree = -1; for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){ nodeArr[(*it).vertex].inDegree--; } topoSortArr.push_back(i); } return topoSortArr; }
测试函数:
1、读取图文件中的数据,图中的数据格式为下面所示:
0,0,1,1 1,0,2,2 2,0,3,1
第1列是边号,第2列是边的起点,第3列是边的终点,第4列是边的权重。
/**************************************************************** * 函数名称:read_file * 功能描述: 读取文件中的图的数据信息 * 参数列表: buff是将文件读取的图信息保存到buff指向的二维数组中 * spec是文件中图最大允许的边的个数 * filename是要打开的图文件 * 返回结果:无 *****************************************************************/ int read_file(char ** const buff, const unsigned int spec, const char * const filename) { FILE *fp = fopen(filename, "r"); if (fp == NULL) { printf("Fail to open file %s, %s.\n", filename, strerror(errno)); return 0; } printf("Open file %s OK.\n", filename); char line[MAX_LINE_LEN + 2]; unsigned int cnt = 0; while ((cnt < spec) && !feof(fp)) { line[0] = 0; fgets(line, MAX_LINE_LEN + 2, fp); if (line[0] == 0) continue; buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2); strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1); buff[cnt][4001] = 0; cnt++; } fclose(fp); printf("There are %d lines in file %s.\n", cnt, filename); return cnt; }
2、释放刚才读取的图的信息
/**************************************************************** * 函数名称:release_buff * 功能描述: 释放刚才读取的文件中的图的数据信息 * 参数列表: buff是指向文件读取的图信息 * valid_item_num是指图中边的个数 * 返回结果:void *****************************************************************/ void release_buff(char ** const buff, const int valid_item_num) { for (int i = 0; i < valid_item_num; i++) free(buff[i]); }
3、主测试函数
int main(int argc, char *argv[]) { char *topo[5000]; int edge_num; char *demand; int demand_num; char *topo_file = argv[1]; edge_num = read_file(topo, 5000, topo_file); if (edge_num == 0) { printf("Please input valid topo file.\n"); return -1; } Graph G(topo, edge_num); G.print(); vector<int> topoSortArr = G.topoSort(); cout << "拓扑排序的结果: "; for(unsigned i = 0; i < topoSortArr.size(); ++i) cout << topoSortArr[i] << " "; cout << endl; release_buff(topo, edge_num); return 0; }
图类的源代码:
#ifndef GRAPH_H
#define GRAPH_H
#include <list>
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iterator>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
using namespace std;
#define MAX_VERTEX_NUM 600
//保存每个顶点信息的数据结构 struct GraphNode{ int vertex;//当前顶点的标号 int inDegree;//当前顶点的入度 int topNum;//当前顶点的拓扑排序的顺序标号 };
//图节点信息
typedef struct Node{
int edge_num;//边号
int src;//源点
int vertex;//自身
int weight;//边的权重
}Node;
/******************************************************* * 类名称: 邻接表图 ********************************************************/ class Graph{ private: int edge_num;//图边的个数 int vertex_num;//图的顶点数目 list<Node> * graph_list;//邻接表 vector<GraphNode> nodeArr;//保存每个顶点信息的数组 public: Graph(){} Graph(char* graph[], int edgenum); ~Graph(); void print(); vector<int> topoSort();//拓扑排序 private: vector<int> get_graph_value(char* graph[], int columns); void addEdge(char* graph[], int columns); };
/************************************************* * 函数名称:topoSort() * 功能描述:对图中的顶点进行拓扑排序 * 参数列表:无 * 返回结果:返回顶点拓扑排序之后的结果 *************************************************/ vector<int> Graph::topoSort() { vector<int> topoSortArr; for(int count = 0; count < vertex_num; ++count){ //找到一个入度为0的顶点 int i; for(i = 0; i < vertex_num; ++i){ if((nodeArr[i].inDegree == 0)&&(nodeArr[i].vertex != -1)) break; } if(i == vertex_num) break; //此时顶点i的入度为0 //删除该点和删除与该点相邻的边 //并将与顶点i相连的顶点的入度减1 nodeArr[i].inDegree = -1; for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){ nodeArr[(*it).vertex].inDegree--; } topoSortArr.push_back(i); } return topoSortArr; }
/*************************************************
* 函数名称:print
* 功能描述:将图的信息以邻接表的形式输出到标准输出
* 参数列表:无
* 返回结果:无
*************************************************/
void Graph::print()
{
cout << "******************************************************************" << endl;
//for(int i = 0 ; i < MAX_VERTEX_NUM; ++i){
for(int i = 0 ; i < vertex_num; ++i){
if(graph_list[i].begin() != graph_list[i].end()){
cout << i << "-->";
for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){
cout << (*it).vertex << "(边号:" << (*it).edge_num << ",权重:" << (*it).weight << ")-->";
}
cout << "NULL" << endl;
}
}
cout << "******************************************************************" << endl;
}
/*************************************************
* 函数名称:get_graph_value
* 功能描述:将图的每一条边的信息保存到一个数组中
* 参数列表: graph:指向图信息的二维数组
columns:图的第几条边
* 返回结果:无
*************************************************/
vector<int> Graph::get_graph_value(char* graph[], int columns)
{
vector<int> v;
char buff[20];
int i = 0, j = 0, val;
memset(buff, 0, 20);
while((graph[columns][i] != '\n') && (graph[columns][i] != '\0')){
if(graph[columns][i] != ','){
buff[j] = graph[columns][i];
j++;
}
else{
j = 0;
val = atoi(buff);
v.push_back(val);
memset(buff, 0, 20);
}
i++;
}
val = atoi(buff);
v.push_back(val);
return v;
}
/*************************************************
* 函数名称:addEdge
* 功能描述:将图的每一条边的信息加入图的邻接表中
* 参数列表:graph:指向图信息的二维数组
columns:图的第几条边
* 返回结果:无
*************************************************/
void Graph::addEdge(char* graph[], int columns)
{
Node node;
vector<int> v = get_graph_value(graph, columns);
node.edge_num = v[0];
node.src = v[1];
node.vertex = v[2];
node.weight = v[3];
//根据顶点的标号,求的总的顶点数目
if(node.vertex > vertex_num)
vertex_num = node.vertex;
//要考虑重复的边,但是边的权重不一样
for(list<Node>::iterator it = graph_list[node.src].begin(); it != graph_list[node.src].end(); ++it){
if((*it).vertex == node.vertex){
if((*it).weight > node.weight){
(*it).weight = node.weight;
}
return;
}
}
//将信息写入到保存每个顶点的数组中
nodeArr[node.src].vertex = node.src;
nodeArr[node.vertex].vertex = node.vertex;
nodeArr[node.vertex].inDegree++;//入度加1
graph_list[node.src].push_back(node);
}
/*************************************************
* 函数名称:构造函数
* 功能描述:以邻接表的形式保存图的信息,并保存必须经过的顶点
* 参数列表:graph:指向图信息的二维数组
edgenum:图的边的个数
* 返回结果:无
*************************************************/
Graph::Graph(char* graph[], int edgenum):nodeArr(MAX_VERTEX_NUM)
{
edge_num = edgenum;
vertex_num = 0;
graph_list = new list<Node>[MAX_VERTEX_NUM+1];
//对保存顶点信息的数组进行初始化,如果vertext=-1表示没有该顶点
for(int i = 0; i < MAX_VERTEX_NUM; ++i){
nodeArr[i].vertex = -1;
nodeArr[i].inDegree = 0;
nodeArr[i].topNum = -1;
}
for(int i = 0; i < edgenum; ++i){
addEdge(graph, i);
}
vertex_num++;
}
/*************************************************
* 函数名称:析构函数
* 功能描述:释放动态分配的内存
* 参数列表:无
* 返回结果:无
*************************************************/
Graph::~Graph()
{
delete[] graph_list;
}
#endif
测试函数的源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <sys/timeb.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include "graphTopoSort.h"
#define MAX_LINE_LEN 4000
int read_file(char ** const buff, const unsigned int spec, const char * const filename);
void release_buff(char ** const buff, const int valid_item_num);
int main(int argc, char *argv[]) { char *topo[5000]; int edge_num; char *demand; int demand_num; char *topo_file = argv[1]; edge_num = read_file(topo, 5000, topo_file); if (edge_num == 0) { printf("Please input valid topo file.\n"); return -1; } Graph G(topo, edge_num); G.print(); vector<int> topoSortArr = G.topoSort(); cout << "拓扑排序的结果: "; for(unsigned i = 0; i < topoSortArr.size(); ++i) cout << topoSortArr[i] << " "; cout << endl; release_buff(topo, edge_num); return 0; }
/**************************************************************** * 函数名称:read_file * 功能描述: 读取文件中的图的数据信息 * 参数列表: buff是将文件读取的图信息保存到buff指向的二维数组中 * spec是文件中图最大允许的边的个数 * filename是要打开的图文件 * 返回结果:无 *****************************************************************/ int read_file(char ** const buff, const unsigned int spec, const char * const filename) { FILE *fp = fopen(filename, "r"); if (fp == NULL) { printf("Fail to open file %s, %s.\n", filename, strerror(errno)); return 0; } printf("Open file %s OK.\n", filename); char line[MAX_LINE_LEN + 2]; unsigned int cnt = 0; while ((cnt < spec) && !feof(fp)) { line[0] = 0; fgets(line, MAX_LINE_LEN + 2, fp); if (line[0] == 0) continue; buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2); strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1); buff[cnt][4001] = 0; cnt++; } fclose(fp); printf("There are %d lines in file %s.\n", cnt, filename); return cnt; }
/**************************************************************** * 函数名称:release_buff * 功能描述: 释放刚才读取的文件中的图的数据信息 * 参数列表: buff是指向文件读取的图信息 * valid_item_num是指图中边的个数 * 返回结果:void *****************************************************************/ void release_buff(char ** const buff, const int valid_item_num) { for (int i = 0; i < valid_item_num; i++) free(buff[i]); }
测试用例:
0,1,2,1 1,1,3,1 2,1,4,1 3,2,4,1 4,2,5,1 5,3,6,1 6,4,3,1 7,4,6,1 8,4,7,1 9,5,4,1 10,5,7,1 11,7,6,1
运行结果:
相关文章推荐
- 排序算法性能比较练习——《数据结构、算法与应用》
- 基本数据结构
- 数据结构------绪论
- 关于程序运行时的数据结构
- 学JS必看-JavaScript数据结构深度剖析
- 数据结构和算法 – 3.堆栈和队列
- 数据结构算法——单链表
- 【数据结构与算法】寻找最大的K个数
- 关系型数据库工作原理-数据结构(翻译自Coding-Geek文章)
- Clojure学习03:数据结构(集合)
- Java数据结构初试之自定义自己的链表结构类
- 循环队列
- 【数据结构】输入二叉树的前序序列,输出二叉树的不同度的结点个数
- 一步一步写数据结构(BST-二叉排序树)
- 集合框架(数据结构之数组和链表)
- 数据结构与算法(一) 交换
- 《常见算法和数据结构》优先队列(3)——堆排序
- 第十周 技术博客发表 数据结构
- 集合框架(数据结构之栈和队列)
- 《常见算法和数据结构》优先队列(2)——二叉堆