图的基本数据结构及算法实现-c语言

1.导入库文件

#include 
#include 
#include 

2.创建图的基本数据类型(元素)

主要有顶点数组、邻接矩阵、顶点数、弧数

#define Node 10
typedef struct
{
    int vex[Node];        //顶点数组
    int arcs[Node][Node]; //邻接矩阵
    int vexNum, arcNum;   //顶点数,弧数
} G, *Graph;

3.创建基本函数

初始化图的基本信息

这里我在函数内直接读取键盘输入的顶点个数 i 来初始化图
初始化顶点数组为 -1 ,邻接矩阵为 0 ,顶点数为 i ,弧为 0

void initGraph(Graph g)
{
    int i;
    printf("input the number of Nodes:\n");
    scanf("%d", &i);
    g->vexNum = i;

    g->arcNum = 0;
    for (int t = 0; t < i; t++)
    {
        g->vex[t] = -1;
    }

    for (int z = 0; z < i; z++)
    {
        for (int k = 0; k < i; k++)
        {
            g->arcs[z][k] = 0;
        }
    }
    return;
}

合法化判断

接受输入<(int)a,(int)b>((int)a,(int)b)类型
修改returnData数组的值
returnData[0]=a returnData[1]=b
并返回合法与否:1/0
注:返回值只是单纯表示结点的值而非在数组中位置
本例中接受任意数字输入,可以理解为结点的名称为该数字(:

int inputValid(char *input, int *returnData)
{ //合法化判断 并且获得结点信息 这里设置的是int类型的数
    int i = 1, j = 0, number = 0;
    char getData[20];
    if (input[0] != '(' && input[0] != '<')
    {
        if (input[0] == '#')
            return 0;
        printf("Wrong input %c\n", input[0]);
        return 0;
    }
    while (input[i] != ',')
    {
        getData[j++] = input[i++];
    }
    for (int t = j - 1, z = 0; t >= 0; t--, z++)
    {
        number += (getData[t] - '0') * pow(10, z);
        getData[t] = 0;
    }
    *returnData = number;
    *returnData++;
    number = 0;
    i++;
    j = 0;
    while (input[i] != '>' && input[i] != ')')
    {
        getData[j++] = input[i++];
    }
    if (i > Node)
        return 0;
    for (int t = j - 1, z = 0; t >= 0; t--, z++)
    {
        number += (getData[t] - '0') * pow(10, z);
        getData[t] = 0;
    }
    *returnData = number;
    return 1;
}

结点位置获取

返回结点在vex[]数组内的下标索引
如果没有找到该结点则返回 -1

int GetNodePosition(Graph g, int inputNode)
{ //结点在数组的下标
    for (int i = 0; i < g->vexNum; i++)
    {
        if (g->vex[i] == inputNode)
            return i;
    }
    return -1;
}

设置结点

这里直接使用该函数读取结点
功能:创建新结点、输入结点的关系来设置邻接矩阵->构建图
直接每轮读取一个字符串,形式:<,>/(,),结合上一个函数进行合法判断
(这里不合法就直接break了,误输入处理就没做233)
读取到#就完成读取任务,结束循环

void SetNode(Graph g)
{ //获得节点的去重集合 写入信息 主要是顺序存储节点 如果节点重复就不存在节点数组里了
    int j = 0, data[2] = {0}, status0 = 0, status1 = 0, position_i, position_j, typeOfGraph;
    char input[20];
    printf("input data the form should be <,> or (,) And those numbers should bigger than 0\n"); //<)这种输入目前不支持
    printf("input # to quit\n");
    scanf("%s", &input);
    for (int i = 0; i < g->vexNum; i++)
    {
        for (int j =0;j<g->vexNum;j++)
            g->arcs[i][j] = 0;
    }
    for (int z = 0; z < g->vexNum; z++)
    {
        g->vex[z] = -1;
    }
    while (inputValid(input, data) && input[0] != '#')
    {
        if (input[0] == '<')
            typeOfGraph = 0; //有向图
        else if (input[0] == '(')
            typeOfGraph = 1; //无向图

        for (int t = 0, count0 = 0, count1 = 0; t < g->vexNum; t++)
        {
            if (data[0] == g->vex[t])
                count0++;
            if (data[1] == g->vex[t] && data[0] != data[1])
                count1++;
            if (t == g->vexNum - 1 && count0 == 0)
                status0++, printf("create new node %d\n", data[0]);
            if (t == g->vexNum - 1 && count1 == 0)
                status1++, printf("create new node %d\n", data[1]);
        }

        if (status0 > 0)
        {
            for (int t = 0; t < g->vexNum; t++)
            {
                if (g->vex[t] == -1)
                {
                    g->vex[t] = data[0];
                    break;
                }
            }
            status0 = 0;
        }
        if (status1 > 0)
        {
            for (int t = 0; t < g->vexNum; t++)
            {
                if (g->vex[t] == -1)
                {
                    g->vex[t] = data[1];
                    break;
                }
            }
            status1 = 0;
        }

        position_i = GetNodePosition(g, data[0]); //节点位置i
        position_j = GetNodePosition(g, data[1]); //节点位置j    (i,j)
        if (typeOfGraph == 0)
        {
            g->arcs[position_i][position_j]++;
            g->arcNum++;
        }
        else if (typeOfGraph == 1)
        {
            g->arcs[position_i][position_j]++;
            g->arcs[position_j][position_i]++;
            g->arcNum++;
        }
        data[0] = 0;
        data[1] = 0;
        fflush(stdin);
        scanf("%s", &input);
    }
}

打印图的信息

输出图的结点数组以及邻接矩阵

void printGraph(Graph g)
{
	int t,n,z;
    for ( t = 0; t < g->vexNum; t++)
    {
        printf("Node%d:%d\n", t, g->vex[t]);
    }
    printf("\n");
    printf("       ");
    for ( n = 0; n < g->vexNum; n++)
    {
        printf("%d ", g->vex[n]);
    }
    printf("\n");
    printf("       ");
    for ( n = 0; n < g->vexNum; n++)
    {
        printf("- ", g->vex[n]);
    }
    printf("\n");
    for ( t = 0; t < g->vexNum; t++)
    {

        printf("Node:%d|", g->vex[t]);
        for ( z = 0; z < g->vexNum; z++)
        {
            printf("%d ", g->arcs[t][z]);
        }
        printf("\n");
    }
}

判断结点之间是否邻接

基于邻接矩阵求解是否邻接
v w 都是结点的名称
返回值在正常情况下是 0 不邻接 /1 邻接
如果结点位置为 -1 则表示结点不存在或初始化错误,返回 -1 当作异常值

int isNeighbour(Graph g, int v, int w)
{ //邻接情况
    int position_i, position_j;
    position_i = GetNodePosition(g, v);
    position_j = GetNodePosition(g, w);
    if (position_j == -1 || position_i == -1)
        return -1;
    if (g->arcs[position_i][position_j] >= 1)
        return 1;
    return 0;
}

出度计算

基于邻接矩阵
也就是求行和

int getOutDegree(Graph g, int v)
{ //计算出度
    int position_v, sum = 0;
    position_v = GetNodePosition(g, v);
    if (position_v == -1)
        return -1;
    for (int i = 0; i < g->vexNum; i++)
    {
        sum += g->arcs[position_v][i];
    }
    return sum;
}

入度计算

基于邻接矩阵
也就是求列和

int getInDegree(Graph g, int v)
{ //计算入度
    int position_v, sum = 0;
    position_v = GetNodePosition(g, v);
    if (position_v == -1)
        return -1;
    for (int i = 0; i < g->vexNum; i++)
    {
        sum += g->arcs[i][position_v];
    }
    return sum;
}

测试用 :)打印矩阵(方阵)

void PrintMatrix(int depth, int (*a)[])
{
    int(*p)[depth];
    p = a;
    for (int i = 0; i < depth; i++)
    {
        for (int j = 0; j < depth; j++)
        {
            printf("%d ", *(*(p + i) + j));
        }
        printf("\n");
    }
}

打印邻接矩阵

void PrintNeighbourMatrix(Graph g)
{
    for (int i = 0; i < g->vexNum; i++)
    {
        for (int j = 0; j < g->vexNum; j++)
        {
            printf("%d ", g->arcs[i][j]);
        }
        printf("\n");
    }
}

方阵加法

会把b矩阵加到a矩阵去

void MatrixAdd(int depth, int (*a)[], int (*b)[])
{ //方阵加法 把后面的矩阵加到前面的矩阵
    int(*p)[depth], (*p2)[depth];
    p = a;
    p2 = b;
    for (int i = 0; i < depth; i++)
    {
        for (int j = 0; j < depth; j++)
        {
            *(*(p + i) + j) += *(*(p2 + i) + j);
        }
    }
}

方阵乘法

把矩阵a与b的乘法结构放到c矩阵里

void MatrixMultiple(int depth, int (*a)[], int (*b)[], int (*c)[])
{
    int(*p)[depth], (*p2)[depth], (*p3)[depth];
    p = a;
    p2 = b;
    p3 = c;
    for (int k = 0; k < depth; k++)
    {
        for (int i = 0; i < depth; i++)
        {
            for (int j = 0; j < depth; j++)
            {
                *(*(p3 + k) + i) += (*(*(p + k) + j)) * (*(*(p2 + j) + i));
            }
        }
    }
}

Bn矩阵

Bn = A + A^2 + A^3 +…
若要求可达矩阵就把矩阵中 >0 的数换成1即可
这里的函数求得结果是Bn矩阵
Bn矩阵具有更多的信息
另:
求解可达矩阵的方法
1.Bn矩阵 >0 元素置换成1
2.布尔矩阵运算
3.Warshall算法

void getReachableMatrix(Graph g, int (*a)[])
{
    int(*p)[g->vexNum], i = 1;
    int b0[g->vexNum][g->vexNum], b1[g->vexNum][g->vexNum], b_init[g->vexNum][g->vexNum];
    p = a;
    for (int k = 0; k < g->vexNum; k++)
    {
        for (int z = 0; z < g->vexNum; z++)
        {
            b0[k][z] = g->arcs[k][z];
            b_init[k][z] = g->arcs[k][z];
            b1[k][z] = 0;
            *(*(p + k) + z) = 0;
        }
    }

    MatrixAdd(g->vexNum, p, b_init);
    PrintMatrix(g->vexNum, p);
    printf("****************\n");
    while (i < g->vexNum)
    {
        for (int k = 0; k < g->vexNum; k++)
        {
            for (int z = 0; z < g->vexNum; z++)
            {
                b1[k][z] = 0;
            }
        }
        MatrixMultiple(g->vexNum, b_init, b0, b1);
        MatrixAdd(g->vexNum, p, b1);
        for (int k = 0; k < g->vexNum; k++)
        {
            for (int z = 0; z < g->vexNum; z++)
            {
                b0[k][z] = b1[k][z];
            }
        }
        i++;
    }

    return;
}

深度优先算法前置函数

求第一个邻接结点(基于邻接矩阵)
这里直接调用了isNeighbour函数
返回第一个邻接结点在结点矩阵中的下标索引
若无结点与之邻接则返回 -1

int firstNeighbourNode(Graph g, int v)
{
    int position_v, position_return;
    position_v = GetNodePosition(g, v);
    for (position_return = 0; position_return < g->vexNum; position_return++)
    {
        if (position_return == position_v)
            continue;
        if (isNeighbour(g, v, g->vex[position_return]))
            return position_return;
    }
    return -1;
}

求下一个邻接结点
返回v下一个邻接结点的下标索引,并且该结点未被访问过
若没找到则返回 -1

int nextNeighbourNode(Graph g, int v, int isvisited[])
{
    int position_v, position_return;
    position_v = GetNodePosition(g, v);
    for (position_return = 0; position_return < g->vexNum; position_return++)
    {
        if (position_return == position_v)
            continue;
        if (isNeighbour(g, v, g->vex[position_return]))
        {
            if (isvisited[position_return] == 0)
                return position_return;
        }
    }
    return -1;
}

基于递归的深度优先遍历算法

利用数组visitedMat[] (值为0/1) 确定当前结点是否被访问过
如果visitedMat[i]=1即被访问过,就不再输出该结点
整体思路:
输出开始结点 -> 开始结点visited 设数组为1 -> w = v的第一个邻接结点位置
while(w 存在)
{
if(w处结点没访问过)调用自己 w作为开始位置
w 取v的下一个邻接结点
}
递归的实质也是利用了栈,如果想实现非递归就使用栈相关应用即可

void deepFirst_traver(Graph g, int v, int visitedMat[])
{                               //深度优先遍历
    int position_v, position_w; //v是进入递归的首结点
    position_v = GetNodePosition(g, v);
    printf("%d\n", v);
    visitedMat[position_v] = 1;
    position_w = firstNeighbourNode(g, v);
    while (position_w != -1)
    {
        if (visitedMat[position_w] == 0)
            deepFirst_traver(g, g->vex[position_w], visitedMat);
        position_w = nextNeighbourNode(g, v, visitedMat);
    }
}

广度优先遍历算法

注:这里需要使用队列
我使用的是上次写的 c语言实现队列基本功能
data类型没改,直接用了char类型的 ︿( ̄︶ ̄)︿
若运行有误调整成int型即可
整体思路:
访问初始结点v0,取其在顶点数组的下标索引入队
打印初始节点
while(队列不空)
{
    队头顶点编号出队
    w = vex[v位置]的第一个邻接结点的位置索引
    while(w 存在){
        if(visited[w]=0 也就是没被访问过){
            打印该节点
            设置visited数组
            w位置入队列
         }
    更新w = vex[v位置] 的下一个结点(未被访问过的)
    }
}

void breadth_FirstSearch(Graph g, int v0,Queue q){
	int position_v,position_j,visited[g->vexNum],i,position;
	char v;
	for(i=0;i<g->vexNum;i++){
		visited[i]=0;
	}

	initQueue(q);
	position_v = GetNodePosition(g,v0);
	printf("%d\n",v0);
	visited[position_v] = 1;
	QueueEnter(q,(char)position_v);
	while(QueueEmpty(q) == 0){
		QueueOut(q,&v);
		position_j = firstNeighbourNode(g,g->vex[v]);

 		i = position_j;
		while(position_j != -1){
			position = position_j;
			if(visited[position] == 0){
				printf("%d\n",g->vex[position]);
				visited[position]=1;
				QueueEnter(q,(char)position);
			}
			position_j = nextNeighbourNode(g,g->vex[v],visited);
			
		}

	}
}

求简单路径

DFS函数可以求解以i为开始结点的所有简单路径
再使用Hamilton函数搜索所有路径
对DFS的修改之处:
计数器 n
访问顶点时会将该顶点的位置索引存储在path数组中
同时计数器 n自增1
递归出口在于 计数器的值 = 结点总数
如果某顶点的全部有邻接关系的结点都访问过,仍然未得到简单路径,就将该顶点置为未访问,计数器自减1
(相当于发现当前结点不合适,那么放弃本次寻找的路径,重新从另一点尝试)

void DFS(Graph g,int i,int path[],int visited[],int *n){
    //深度优先算法找包含全部顶点的简单路径
    //对于单个i(函数未进行递归时),本函数可以找到以位置i为起点的所有简单路径
    int t,j;
    visited[i] = 1;
    path[*n]=i;
    (*n)++;
    //printf("---%d\n",*n);
    if((*n)==g->vexNum){
        for(t=0;t<g->vexNum-1;t++){
            printf("%d->",g->vex[path[t]]);
        }
        printf("%d\n",g->vex[path[t]]);
    }
    for(j=0;j<g->vexNum;j++){
        if(g->arcs[i][j] && !visited[j])DFS(g,j,path,visited,n);
    }
    visited[i]=0;
    (*n)--;
}

void Hamilton(Graph g){
    //哈密尔顿链
    int i,n=0;
    int visited[g->vexNum],path[g->vexNum];
    for (i=0;i<g->vexNum;i++){
        visited[i] = 0;
        path[i] = 0;
    }
	for(i=0;i<g->vexNum;i++){
		if(!visited[i])DFS(g,i,path,visited,&n);
	}

}

代码部分I/O漏洞还是存在的
本例只为尝试实现一些基本的pseudo-code
(๑•̀ㅂ•́)و✧

你可能感兴趣的:(数据结构,算法,队列,c语言,图论)