数据结构——图的存储结构及遍历

图的存储结构

邻接矩阵法
  • 使用一个一维数组存储图的顶点信息,用一个二维数组存储图中边的信息
  • 结点数为 n n n的图 G = ( V , E ) G=(V, E) G=(V,E)的邻接矩阵 A A A n ∗ n n*n nn
  • G G G的顶点编号为 v 1 , v 2 , … , v n v_1,v_2,…,v_n v1,v2,,vn。若 ( v i , v j ) ∈ E (v_i,v_j)\in E (vi,vj)E,则 A [ i ] [ j ] = 1 A[i][j]=1 A[i][j]=1,否则 A [ i ] [ j ] = 0 A[i][j]=0 A[i][j]=0
  • 对于带权图,若 ( v i , v j ) ∈ E (v_i,v_j)\in E (vi,vj)E,则 A [ i ] [ j ] = w i j A[i][j]=w_{ij} A[i][j]=wij,否则 A [ i ] [ j ] = 0 或 ∞ A[i][j]=0或\infty A[i][j]=0
  • 有向图和无向图都可用邻接矩阵法,且无向图的邻接矩阵为对称矩阵(可使用压缩存储

存储结构定义如下:

#define MaxVertexNum 100		// 顶点数目的最大值
typedef char VertexType;
typedef int EdgeType;
typedef struct{
	VertexType Vex[MaxVertexNum];				// 顶点表
	EdgeType Edge[MaxVertexNum][MaxVertexNum];	// 邻接矩阵
	int vexnum, arcnum;							// 顶点数及弧数
}MGraph;
邻接表法
  • 当一个图为稀疏图时,使用邻接矩阵会浪费大量存储空间,使用邻接表法可以减少这种不必要的浪费
  • 对图 G G G中每个顶点 v i v_i vi建立一个单链表(边表),第 i i i个单链表中的结点表示依附于顶点 v i v_i vi的边
  • 边表的头指针和顶点的数据信息采用顺序存储

存储结构定义如下:

#define MaxVertexNum 100
typedef struct ArcNode{			// 边表结点
	int adjvex;					// 弧指向的顶点位置
	struct ArcNode *next;		// 指向下一条弧
	InfoType info;				// 边权值
}ArcNode;
typedef struct VNode{			// 顶点表结点
	VertexType data;			// 顶点信息
	ArcNode *first;				// 指向第一条依附于该顶点的弧的指针
}VNode, AdjList[MaxVertexNum];
typedef struct{
	AdjList vertices;			// 邻接表
	int vexnum, arcnum;			// 顶点数和弧数
}ALGraph;
十字链表法
  • 十字链表是有向图的一种链式存储结构
  • 在十字链表中,对应于有向图中的每条弧有一个结点,对应于每个顶点也有一个结点

存储结构定义如下:

#define MaxVertexNum 100
typedef struct ArcNode{
	int tailvex, headvex;			// 尾域和头域,指向弧尾和弧头
	struct ArcNode *hlink, *tlink;	// hlink指向弧头相同的下一条弧,tlink指向弧尾相同的下一条弧
	InfoType info;					// 弧的权值
}ArcNode;
typdef struct VNode{
	VertexType data;				// 顶点相关数据信息
	ArcNode *firstin, *firstout;	// firstin和firstout分别指向以该顶点为弧头或弧尾的第一个弧结点
}VNode, AdjList[MaxVertexNum];
typedef struct{
	AdjList vertices;				// 十字链表
	int vexnum, arcnum;				// 顶点数和弧数
}OLGraph;

图的遍历

广度优先遍历(BFS)
  • 基本思想:
    1. 首先访问起始顶点 v v v,然后从 v v v出发,依次访问 v v v的各个未被访问的邻接顶点 w 1 , w 2 , … , w i w_1,w_2,…,w_i w1,w2,,wi,然后依次访问 w 1 , w 2 , … , w i w_1,w_2,…,w_i w1,w2,,wi的所有未被访问过的邻接顶点,依次类推,直至图中所有顶点都被访问为止。
    2. 若此时图中还有顶点未被访问,则选择其中一个未被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问为止。
深度优先遍历(DFS)
  • 基本思想:
    1. 首先访问起始顶点 v v v,然后从 v v v出发,访问与 v v v邻接的未被访问的任一顶点 w 1 w_1 w1,再访问 w 1 w_1 w1的未被访问过的任一邻接顶点 w 2 w_2 w2,依次类推,当不能继续向下访问时,依次退回到最近被访问的顶点,若它有其他邻接顶点未被访问,则从该结点继续上述过程,直至图中所有顶点都被访问为止。
    2. 若此时图中还有顶点未被访问,则选择其中一个未被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问为止。
基于邻接表的图的遍历代码实现
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h> 
#define MAX_VERTEX_NUM 20
typedef char VertexType;
typedef int VRType;
typedef int InfoType;
typedef int QElemType;


typedef struct ArcNode
{
    int adjvex;
    struct ArcNode *nextarc;
    InfoType *info;
 }ArcNode;

 typedef struct VNode
 {
    VertexType data;
    ArcNode *firstarc;
 }VNode,AdjList[MAX_VERTEX_NUM];
 
 typedef struct
 {
    AdjList vertices;    
    int vexnum,arcnum;
 }ALGraph;

typedef struct QNode
{
    QElemType data;
    struct QNode *qnext;
}QNode,*PQNode;

typedef struct Queue
{
    PQNode front;
    PQNode rear;
}Queue,*PQueue;

PQueue initQueue()
{
    PQueue pqueue = (PQueue)malloc(sizeof(Queue));
    PQNode pqnode = (PQNode)malloc(sizeof(QNode));
    if(pqnode==NULL)
    {
        printf("队列头空间申请失败!\n");
        exit(-1);
    }
    pqueue->front = pqueue->rear = pqnode;
    pqnode->qnext = NULL;
    return pqueue;
}

void enQueue(PQueue pqueue,QElemType data)
{
    PQNode pqnode = (PQNode)malloc(sizeof(QNode));
    if(pqnode==NULL)
    {
        printf("队列结点申请失败!\n");
        exit(-1);
    }
    pqnode->data = data;
    pqnode->qnext = NULL;
    pqueue->rear->qnext = pqnode;
    pqueue->rear = pqnode;
 } 

bool isEmpty(PQueue pqueue)
{
    if(pqueue->front == pqueue->rear)
        return true;
    return false;
 } 

QElemType deQueue(PQueue pqueue)
{
    if(isEmpty(pqueue))
    {
        printf("队列为空\n");
        exit(-1);
    }

    PQNode pqnode = pqueue->front->qnext;
    pqueue->front->qnext = pqnode->qnext;
    if(pqnode == pqueue->rear)
        pqueue->rear = pqueue->front;
    QElemType data = pqnode->data;
    free(pqnode);
    return data;

}

int locateVex(ALGraph alg,char v)
{
    int i;
    for(i=0;i<alg.vexnum;i++)
    {
        if(alg.vertices[i].data == v)
        return i;
    }
    return -1;
 } 

void createALGraph(ALGraph *alg)
{
    int i,j,v,k;
    printf("输入图的顶点数和边数\n");
    scanf("%d %d",&(*alg).vexnum,&(*alg).arcnum);
    printf("输入顶点名称\n");
    //fflush(stdin); 
    getchar();
    for(i = 0;i<(*alg).vexnum;i++)
    {
        printf("输入第%d个顶点名称:",i);
        scanf("%c",&(*alg).vertices[i].data); 
        (*alg).vertices[i].firstarc = NULL;
        getchar();
    }

    printf("输入图的边信息\n");
    char v1,v2;
    ArcNode *s,*p;
    for(k = 0;k<(*alg).arcnum;k++)
    {
        printf("输入第%d条边的两个节点v1 v2:",k);
        scanf("%c %c",&v1,&v2);
        i = locateVex((*alg),v1);
        j = locateVex((*alg),v2);
        p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = j;
        p->nextarc = NULL;
        if((*alg).vertices[i].firstarc==NULL)
        {
            (*alg).vertices[i].firstarc = p;
        }
        else
        {
            s = (*alg).vertices[i].firstarc;
            while(s->nextarc!=NULL)
                s = s->nextarc;
            s->nextarc = p;
        }

        p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = i;
        p->nextarc = NULL;
        if((*alg).vertices[j].firstarc==NULL)
        {
            (*alg).vertices[j].firstarc = p;
        }
        else
        {
            s = (*alg).vertices[j].firstarc;
            while(s->nextarc!=NULL)
                s = s->nextarc;
            s->nextarc = p;
        }
    getchar();
    } 
 } 

bool visited[MAX_VERTEX_NUM];

void DFS(ALGraph alg,int v)
{
    ArcNode *p;
    visited[v] = true;
    printf("%c ",alg.vertices[v].data);
    for(p = alg.vertices[v].firstarc;p!=NULL;p = p->nextarc)
    {
        if(!visited[p->adjvex])
            DFS(alg,p->adjvex);
     } 
 } 

void DFSTraverse(ALGraph alg)
{
    printf("深度优先遍历序列:");
    int v;
    for(v=0;v<alg.vexnum;v++)
        visited[v] = false;
    for(v=0;v<alg.vexnum;v++)
    {
        if(!visited[v])
            DFS(alg,v);
     } 
}

void BFSTraverse(ALGraph alg)
{
    printf("广度优先遍历序列:");
    PQueue pqueue = initQueue();
    ArcNode *p;
    int i;
    QElemType v;
    for(i=0;i<alg.vexnum;i++)
        visited[i] = false;
    for(i=0;i<alg.vexnum;i++)
    {
        if(!visited[i])
        {
            visited[i] = true;
            printf("%c ",alg.vertices[i].data);
            enQueue(pqueue,i);
            while(!isEmpty(pqueue))
            {
                v = deQueue(pqueue);
                for(p = alg.vertices[v].firstarc;p!=NULL;p = p->nextarc)
                {
                    if(!visited[p->adjvex])
                    {
                        printf("%c ",alg.vertices[p->adjvex].data);
                        visited[p->adjvex] = true;
                        enQueue(pqueue,p->adjvex);
                    }
                }
            }
        }
    }

 } 
int main(int argc, char *argv[]) {
    ALGraph alg;
    createALGraph(&alg);
    DFSTraverse(alg);
    printf("\n"); 
    BFSTraverse(alg);
    printf("\n"); 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值