数据结构课设——有向图的深度、广度优先遍历及拓扑排序

任务:给定一个有向图,实现图的深度优先, 广度优先遍历算法,拓扑有序序列,并输出相关结果。

功能要求:输入图的基本信息,并建立图存储结构(有相应提示),输出遍历序列,然后进行拓扑排序,并测试该图是否为有向无环图,并输出拓扑序列。

按照惯例,先上代码,注释超详细:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#pragma warning(disable:4996)
#define Max 20//定义数组元素最大个数(顶点最大个数)
typedef struct node//边表结点
{
    int adjvex;//该边所指向结点对应的下标
    struct node* next;//该边所指向下一个结点的指针
}eNode;
typedef struct headnode//顶点表结点
{
    int in;//顶点入度
    char vertex;//顶点数据
    eNode* firstedge;//指向第一条边的指针,边表头指针
}hNode;
typedef struct//邻接表(图)
{
    hNode adjlist[Max];//以数组的形式存储
    int n, e;//顶点数,边数
}linkG;

//以邻接表的存储结构创建图
linkG* creat(linkG* g)
{
    int i, k;
    eNode* s;//边表结点
    int n1, e1;
    char ch;
    g = (linkG*)malloc(sizeof(linkG));//申请结点空间
    printf("请输入顶点数和边数:");
    scanf("%d%d", &n1, &e1);
    g->n = n1;
    g->e = e1;
    printf("顶点数:%d    边数:%d\n", g->n, g->e);
    printf("请输入顶点信息(字母):");
    getchar();//因为接下来要输入字符串,所以getchar用于承接上一条命令的结束符
    for (i = 0; i < n1; i++)
    {
        scanf("%c", &ch);
        g->adjlist[i].vertex = ch;//获得该顶点数据
        g->adjlist[i].firstedge = NULL;//第一条边设为空
    }
    printf("\n打印顶点下标及顶点数据:\n");
    for (i = 0; i < g->n; i++)//循环打印顶点下标及顶点数据
    {
        printf("顶点下标:%d  顶点数据:%c\n", i, g->adjlist[i].vertex);
    }
    getchar();
    int i1, j1;//相连接的两个顶点序号
    for (k = 0; k < e1; k++)//建立边表
    {

        printf("请输入对<i,j>(空格分隔):");
        scanf("%d%d", &i1, &j1);
        s = (eNode*)malloc(sizeof(eNode));//申请边结点空间
        s->adjvex = j1;//边所指向结点的位置,下标为j1
        s->next = g->adjlist[i1].firstedge;//将当前s的指针指向当前顶点上指向的结点
        g->adjlist[i1].firstedge = s;//将当前顶点的指针指向s
    }
    return g;//返回指针g
}
int visited[Max];//标记是否访问
void DFS(linkG* g, int i)//深度优先遍历
{
    eNode* p;
    printf("%c ", g->adjlist[i].vertex);
    visited[i] = 1;//将已访问过的顶点visited值改为1
    p = g->adjlist[i].firstedge;//p指向顶点i的第一条边
    while (p)//p不为NULL时(边存在)
    {
        if (visited[p->adjvex] != 1)//如果没有被访问
        {
            DFS(g, p->adjvex);//递归
        }
        p = p->next;//p指向下一个结点
    }
}
void DFSTravel(linkG* g)//遍历非连通图
{
    int i;
    printf("深度优先遍历;\n");
    //printf("%d\n",g->n);
    for (i = 0; i < g->n; i++)//初始化为0
    {
        visited[i] = 0;
    }
    for (i = 0; i < g->n; i++)//对每个顶点做循环
    {
        if (!visited[i])//如果没有被访问
        {
            DFS(g, i);//调用DFS函数
        }
    }
}
void BFS(linkG* g, int i)//广度优先遍历
{
    int j;
    eNode* p;
    int q[Max], front = 0, rear = 0;//建立顺序队列用来存储,并初始化
    printf("%c ", g->adjlist[i].vertex);
    visited[i] = 1;//将已经访问过的改成1
    rear = (rear + 1) % Max;//普通顺序队列的话,这里是rear++
    q[rear] = i;//当前顶点(下标)队尾进队
    while (front != rear)//队列非空
    {
        front = (front + 1) % Max;//循环队列,顶点出队
        j = q[front];
        p = g->adjlist[j].firstedge;//p指向出队顶点j的第一条边
        while (p != NULL)
        {
            if (visited[p->adjvex] == 0)//如果未被访问
            {
                printf("%c ", g->adjlist[p->adjvex].vertex);
                visited[p->adjvex] = 1;//将该顶点标记数组值改为1
                rear = (rear + 1) % Max;//循环队列
                q[rear] = p->adjvex;//该顶点进队
            }
            p = p->next;//指向下一个结点
        }
    }
}
void BFSTravel(linkG* g)//遍历非连通图
{
    int i;
    printf("广度优先遍历:\n");
    for (i = 0; i < g->n; i++)//初始化为0
    {
        visited[i] = 0;
    }
    for (i = 0; i < g->n; i++)//对每个顶点做循环
    {
        if (!visited[i])//如果没有被访问过
        {
            BFS(g, i);//调用BFS函数
        }
    }
}
//因为拓扑排序要求入度为0,所以需要先求出每个顶点的入度
void inDegree(linkG* g)//求图顶点入度
{
    eNode* p;
    int i;
    for (i = 0; i < g->n; i++)//循环将顶点入度初始化为0
    {
        g->adjlist[i].in = 0;
    }
    for (i = 0; i < g->n; i++)//循环每个顶点
    {
        p = g->adjlist[i].firstedge;//获取第i个链表第1个边结点指针
        while (p != NULL)///当p不为空(边存在)
        {
            g->adjlist[p->adjvex].in++;//该边终点结点入度+1
            p = p->next;//p指向下一个边结点
        }
        printf("顶点%c的入度为:%d\n", g->adjlist[i].vertex, g->adjlist[i].in);
    }

}
void topo_sort(linkG *g)//拓扑排序
{
    eNode* p;
    int i, k, gettop;
    int top = 0;//用于栈指针的下标索引
    int count = 0;//用于统计输出顶点的个数
    int* stack=(int *)malloc(g->n*sizeof(int));//用于存储入度为0的顶点
    for (i=0;i<g->n;i++)//第一次搜索入度为0的顶点
    {
        if (g->adjlist[i].in==0)
        {
            stack[++top] = i;//将入度为0的顶点进栈
        }
    }
    while (top!=0)//当栈不为空时
    {
        gettop = stack[top--];//出栈,并保存栈顶元素(下标)
        printf("%c ",g->adjlist[gettop].vertex);
        count++;//统计顶点
        //接下来是将邻接点的入度减一,并判断该点入度是否为0
        p = g->adjlist[gettop].firstedge;//p指向该顶点的第一条边的指针
        while (p)//当p不为空时
        {
            k = p->adjvex;//相连接的顶点(下标)
            g->adjlist[k].in--;//该顶点入度减一
            if (g->adjlist[k].in==0)
            {
                stack[++top] = k;//如果入度为0,则进栈
            }
            p = p->next;//指向下一条边
        }
    }
    if (count<g->n)//如果输出的顶点数少于总顶点数,则表示有环
    {
        printf("\n有回路!\n");
    }
    free(stack);//释放空间
}
void menu()//菜单
{
    system("cls");//清屏函数
    printf("************************************************\n");
    printf("*             1.建立图                         *\n");
    printf("*             2.深度优先遍历                   *\n");
    printf("*             3.广度优先遍历                   *\n");
    printf("*             4.求出顶点入度                   *\n");
    printf("*             5.拓扑排序                       *\n");
    printf("*             6.退出                           *\n");
    printf("************************************************\n");
}
int main()
{
    linkG* g = NULL;
    int c;
    while (1)
    {
        menu();
        printf("请选择:");
        scanf("%d", &c);
        switch (c)
        {
        case 1:g = creat(g); system("pause");
            break;
        case 2:DFSTravel(g); system("pause");
            break;
        case 3:BFSTravel(g); system("pause");
            break;
        case 4:inDegree(g); system("pause");
            break;
        case 5:topo_sort(g); system("pause");
            break;
        case 6:exit(0);
            break;
        }
    }
    return 0;
}

实验用图:

运行结果:

关于深度优先遍历

  a.从图中某个顶点v出发,访问v。

  b.找到刚访问过得顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直至刚访问的顶点没有未被访问的邻接点为止。

  c.返回前一个访问过得且扔有未被访问的邻接点的顶点,找到该顶点的下一个未被访问的邻接点,访问该顶点。

  d.重复步骤2,3,直至图中所有顶点都被访问过。

时间复杂性:采用邻接表存储方法,它的e条边所对应的边结点的总个数为2e,调用DFS的时间为O(e)。此时算法的时间复杂度为O(n+e).当n<=e时,若不计置visited的时间,则整个深度优先搜索所需要的时间为O(e)。

关于广度优先遍历

首先访问指定出发顶点,然后依次访问该顶点的所有未被访问过的顶点,再接下来访问邻接点的未被访问过的邻接点,以此类推。由此可见,实现这个过程需要设置一个队列结构。

广度优先搜索是按层来处理顶点,距离开始点最近的那些顶点首先被访问,而最远的那些顶点则最后被访问,这个和树的层序变量很像,BFS的代码使用了一个队列。搜索步骤: 
  a .首先选择一个顶点作为起始顶点,并将其visited设为0 
  b. 将起始顶点放入队列中。 
  c. 从队列首部选出一个顶点,并找出所有与之邻接的顶点,将找到的邻接顶点放入队列尾部,将已访问过顶点visited设置为1,没访问过的顶点是0。
  d. 按照同样的方法处理队列中的下一个顶点。

时间复杂性:从算法中可知,每个顶点最多进队一次,n个顶点一共有n次进队操作,由于遍历的过程实际上是通过边或弧寻找邻接点的过程,因此广度优先搜索算法的时间复杂度与深度优先搜索算法地时间复杂度相同,两者空间复杂度均为O(n)。

关于拓扑排序

在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。

先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。

一直做改操作,直到所有的节点都被分离出来。

如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。

时间复杂性:如果AOV网络有n个顶点,e条边,在拓扑排序的过程中,搜索入度为零的顶点所需的时间是O(n)。在正常情况下,每个顶点进一次栈,出一次栈,所需时间O(n)。每个顶点入度减1的运算共执行了e次。所以总的时间复杂为O(n+e)。

原文地址:https://www.cnblogs.com/nqqqy/p/12243875.html

时间: 2024-10-07 15:17:27

数据结构课设——有向图的深度、广度优先遍历及拓扑排序的相关文章

ATM取款机模拟——数据结构课设

今天帮人写的第二篇课设 , ;-) 机智的窝 要求:大概说一下吧,就是要创建一个用户(初始化一账户),模拟ATM的业务(取款,100的整数倍,改密               码,查余额,等等,各种简单繁琐的操作 ;-) ) 直接贴代码吧: #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <string> using name

排序算法及其比较--数据结构课设

 排序算法及其比较 课程设计报告 一. 设计内容 编程实现希尔.快速.堆排序.归并排序算法,并利用程序统计每种算法的执行时间.要求随机产生10000(或50000. 100000. 200000,由用户选择)个数据存入数据文件,然后读数据文件,分别采用不同排序方法进行排序,将结果存入另一个文件中. 二. 设计思想描述 1.  总思想 本程序采用模块化设计思想,分为产生随机数模块,计时模块,写入磁盘模块,读出磁盘模块,希尔排序模块,快速排序模块,堆排序模块,归并排序模块,计时模块.对常见的4 种经

数据结构与算法——有向无环图的拓扑排序C++实现

拓扑排序简介: 拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从Vi到Vj的路径,那么在排序中Vi在Vj的前面. 如果图中含有回路,那么拓扑排序是不可能的.此外,拓扑排序不必是唯一的,任何合理的排序都可以. 对于上面的无环图:v1,v2,v5,v4,v3,v7,v6和v1,v2,v5,v4,v7,v3,v6都是合理的拓扑排序. 一个简单的求拓扑排序的思路: 1.先找出任意一个没有入边的顶点 2.然后显出该点,并将它和它邻接的所有的边全部删除. 3.然后,对图中其它部分做同样的处理.

刁肥宅数据结构课设:布隆过滤器的实践与应用(V1.0)

课程编号:                     课程性质:          数据结构课程设计报告         学院:         合肥工业大学            班级:         物联网工程17-2班        姓名:         刁肥宅               学号:         201721800X           教师:         周波                           2018年 8月 10日   至  2018年 8

数据结构课设

p127 kmp字符串匹配算法 查找串a在串b中出现了多少次 时间复杂度o(n) http://paste.ubuntu.com/13771972/ 测试数据 5HAHAHAHAWQNWQNADAADADADABABABBBABABABABABABABABBDADADDAADAADDAAADAAD P199堆排序 输入A代表将元素加入堆,T代表输出并取走堆中的最大元素 时间复杂度nlgn代码http://paste.ubuntu.com/13773221/ 测试数据 5A 77751A 1329

JavaScript实现图的深度广度优先遍历

function Graph(v) { this.vertices = v; //初始化顶点 this.edges = 0; //边数先设置为0 this.adj = []; //为每一个顶点准备一个链表,表示它和所有节点的关系 for (var i = 0; i < this.vertices.length; i++) { this.adj[i] = []; this.adj[i].push(""); } this.addEdge = addEdge; this.toStrin

刁肥宅数据结构课设“布隆过滤器的实践与应用”源代码(v1.0,永不上交)

代码很简单,写了一些注释:加上注释看就很清楚了. 文件bloomfilter.cpp: 1 #include "bloomfilter.h" 2 3 // return a hash range from 0 to 79999 4 int hash(const char* str, int index) 5 { 6 int hash = 1; 7 int seed = 12345; 8 int curr; 9 switch(index) 10 { 11 case 0:{ 12 whil

图的遍历:拓扑排序

[需要解决的问题] 对一个有向无环图(DAG)拓扑排序. 拓扑排序就是,对图上的点进行排序,使得排在后面的点不能通过一条路径到前面的点. 比如下面这个图: 其中一个拓扑排序是1,2,3,4,5:而1,2,5,3,4就不行,因为3能到达5. 因此可以得出: 用一个队列实现,先把入度为0的点放入队列,每次将队首元素加入ans[i-j]中,删除队首元素,并删除该点连接的所有边,于是每次删除一个点都会产生一个新的入度为0的点,再把这个点插入队列,递归即可. 拓扑排序 模板如下: //复杂度:O(N+M)

7-9-有向图无环拓扑排序-图-第7章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第7章  图 - 有向无环图拓扑排序 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Status.h.SequenceStack.c.ALGraph.c