十字链表的创建与遍历

添加链接描述

运行了一下上面博客中的代码 ,也对其进行了标注.在运行代码的时候出现了一些错误 ,在代码中标注了 也进行了改正.下面附上代码:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define MaxVex 20
typedef int EdgeType;//定义弧信息的类型
typedef char VertexType;//定义顶点的类型
typedef struct EdgeNode//弧结点的定义
{
int tailvex;//弧尾结点的下标
int headvex;//弧头结点的下标
struct EdgeNode *headlink;//指向弧头相同的下一条弧的链域
struct EdgeNode *taillink;//指向弧尾相同的下一条弧的链域
EdgeType info;//弧中所含有的信息  可以是权值等
}EdgeNode;
typedef struct VertexNode//顶点结点的定义
{
int index;//下标值
VertexType data;//顶点内容
EdgeNode *firstout;//顶点的第一条出弧
EdgeNode *firstin;//顶点的第一条入弧
}VertexNode;
typedef struct OLGraph  //十字链表存储结构的有向图定义
{
VertexNode vertices[MaxVex];
int vexnum;//顶点数 
int arcnum;//边数
}OLGraph;
typedef struct Queue//循环队列的定义
{
int data[MaxVex];
int front;//队头
int rear;//队尾
}Queue;
typedef struct Stack//栈的定义
{
int data[MaxVex];
int top;//栈头
int base;//栈底
}Stack;
//初始化栈
void  InitStack(Stack *s){
//初始化的时候  将栈头和栈尾都设置为0
s->top=0;
s->base=0;
}
bool is_stack_empty(Stack *s)//判断是否是空栈
{
if(s->base==s->top){//如果栈头与栈尾同时指向栈尾 那么就证明这个栈是空栈
return true;
}
return false;
}
bool is_stack_full(Stack *s){
if((s->top)-(s->base)+1==MaxVex){//如果栈头的位置减去栈尾的位置等于栈所能容纳的最大数目 表明这个栈已经满了
return true;
}
return false;
}
//入栈
void  Push(Stack *S,int elem)
{
if(!is_stack_full(S)){
S->data[S->top++]=elem;//将elem这个元素放入到栈中 ,且栈头呈现递增的方式
}
}
void Pop(Stack *S,int elem){
if(!is_stack_empty(S)){
elem=S->data[--S->top];//这里的elem是用来存放栈的头元素的值的,出栈  栈头逐渐下降
}
}
//初始化队列
void InitQueue(Queue *Q){
//初始化栈的时候 栈头与栈尾部都指向尾部
Q->front=0;
Q->rear=0;
}
bool is_queue_empty(Queue *Q)
{
//判断是否是空栈  如果两个同时在栈的底部  那么就可以证明这个栈是一个空栈
if(Q->front==Q->rear){
return true;
}
return false;
}
bool is_queue_full(Queue *Q){
if((Q->rear+1)%MaxVex==Q->front){
//判断这个栈是满了  主要是运用取余数的方式来判断
return true;
}
return false;
}
//增加队列
void EnQueue(Queue *Q,int elem){
//将元素elem加入到队列中去
if(!is_queue_full(Q)){
//从尾部开始增加
Q->data[Q->rear]=elem;
Q->rear=(Q->rear+1)%MaxVex;
}
}
void DeQueue(Queue *Q,int *elem){
//由于queue队列是先进先出的结构  所以是从头部出来
if(!is_queue_empty(Q)){
*elem=Q->data[Q->front];//elem用于接受从队列出来的元素
Q->front=(Q->front+1)%MaxVex;
}
}
bool visited[MaxVex];//对于图的深度遍历的递归算法  需要用到全局变量表示这个顶点是否被访问过
void CreateGraph(OLGraph *G){//创建图
//输入弧的数目与顶点的数目
G->vexnum=6;//顶点数
G->arcnum=7;//弧的数目 
//输入顶点信息
printf("请输人顶点信息");
for(int i=0;i<G->vexnum;i++){
scanf("%c",&G->vertices[i].data);//输入顶点的数据信息
G->vertices[i].index=i;//下标值,这是在顶点数组中的下标值
//在初始化时的时候  将这两个指针都指向空
G->vertices[i].firstin=NULL;
G->vertices[i].firstout=NULL;
}
//输入弧的信息
printf("请输入边的信息:");
for(int j=0;j<G->arcnum;j++){
//先给弧分配内存空间
EdgeNode *edge=(EdgeNode *)malloc(sizeof(EdgeNode));
//初始化的时候  将这两个链域都指向空
edge->taillink=NULL;
edge->headlink=NULL;
//输入这条弧的头顶点和尾顶点
scanf("%d%d",&edge->tailvex,&edge->headvex);
//将该顶点插入到当前顶点的出弧中,当前顶点是它的尾顶点
EdgeNode *p=G->vertices[edge->tailvex].firstout;
if(p==NULL)//第一次插入边结点,由于在初始化的时候指向的是空指针
{
G->vertices[edge->tailvex].firstout=edge;//将这条弧设置位尾顶点的一个出弧
}else{
//如果尾顶点的firstout链域不是空,
while(p!=NULL){
if(p->taillink=NULL){
break;
}
p=p->taillink;//将p指向的相同尾部的链域 taillink
}
p->taillink=edge;//再将这
}
//将该弧插入到当前头顶点的入边弧中
//先将q直向头顶点的入边域
EdgeNode *q=G->vertices[edge->headvex].firstin;
if(q==NULL){//先判断是否是空,因为在初始化的时候我们将其设置为空
G->vertices[edge->headvex].firstin=edge;//将这条弧设置位头顶点的第一个入边弧
}
else{
while(q->headlink!=NULL){//如果这个头顶点的相同顶点域不为空
if(q->headlink==NULL){
break;
}
q=q->headlink;
}
q->headlink=edge;//将这条弧加入到与头顶点相同顶点的链域弧中
}
}
}
//这是初始化弧访问标志  一开始都将其初始化位false 也就是未访问状态
void InitVisit(OLGraph *G,bool *visited){
for(int i=0;i<G->vexnum;i++){
visited[i]=false;
}
}
//深度优先遍历 递归实现  这可以针对连通图
//这种访问方式  是从图中的一点 然后遍历整个图
void DFS_Recursion(OLGraph *G,int v){
if(!visited[v]){//先判断给出的这个有没有被访问
printf("%c",G->vertices[v].data);//输出这个点的信息
visited[v]=true;//将这个点标志位已经访问过了
}
//访问这个点的第一个出边
EdgeNode *edge=G->vertices[v].firstout;
while(edge!=NULL){//如果出边存在
if(!visited[edge->headvex]){//访问这个出边的头顶点
DFS_Recursion(G,edge->headvex);//这里使用递归 进而遍历整个图
visited[edge->headvex]=true;//将其标志位访问过了
}
edge=edge->taillink;//再讲这个边指向具有相同起点的下一条边
}
}
//针对非连通图  
//对于非连通图 对其每个顶点采用递归遍历
void DFS_Recurison_Traverse(OLGraph *G){
for(int i=0;i<G->vexnum;i++){
if(!visited[i]){
DFS_Recursion(G,i);
}
}
}
//深度优先遍历的非递归算法
void DFS(OLGraph*G,int v,bool *visited){
int i=0;
Stack S;
InitStack(&S);//初始化栈
Push(&S,v);//将v放入到栈中
while(!is_stack_empty(&S))
{
//再将放入的顶点出栈
Pop(&S,i);//与原文相比  这里可以直接用i,如果使用引用的话  会报错
if(!visited[i]){//判断这个点有没有被访问过
printf("%c",G->vertices[i].data);//输出这个点的信息
visited[i]=true;//标记为已经读过了
}
//令p为该顶点的第一条出边
EdgeNode *edge=G->vertices[i].firstout;
while(edge!=NULL){
if(!visited[edge->headvex])
{//将该条边的头顶点放入到栈中 退回到202行开始循环
Push(&S,edge->headvex);
}
//再将弧指向相同的尾顶点
edge=edge->taillink;
}
}
}
//非递归遍历的图的深度优先遍历
void DFS_Traverse(OLGraph *G){
bool visited[MaxVex];//表示是否已经被访问
for(int i=0;i<G->vexnum;i++){
visited[i]=false;//初始化  表示都未被访问
}
for(int i=0;i<G->vexnum;i++){//与原程序相比  这里我们直接改成了 int i=0;原程序直接写成i=0;这样会报错  提示范围出错
{
//这里与前面十分类似 都是使用递归遍历每一个顶点  
if(!visited[i]){
DFS(G,i,visited);
visited[i]=true;
}
}
}
}
//存储结构位十字链表的图的宽度优先遍历
void BFS(OLGraph *G,int v,bool *visited){
int i=0;
Queue Q;
InitQueue(&Q);//初始化队列
EnQueue(&Q,v);//将其放入到队列中
while(!is_queue_empty(&Q)){
DeQueue(&Q,&i);//将其从队列出队
if(!visited[i]){
printf("%c",G->vertices[i].data);//输出顶点信息
visited[i]=true;//标记已经访问过了
}
//这里与前面的代码就十分相似
EdgeNode *edge=G->vertices[i].firstout;
while(edge!=NULL){
if(!visited[edge->headvex]){
EnQueue(&Q,edge->headvex);
}
edge=edge->taillink;
}
}
}
void BFS_Traverse(OLGraph *G){
bool visited[MaxVex];
for(int i=0;i<G->vexnum;i++){
visited[i]=false;//初始化 表示都未被访问
}
for(int i=0;i<G->vexnum;i++){//这里我们也做了一些修改
BFS(G,i,visited);
visited[i]=true;
}
}
int main(){
OLGraph G;
CreateGraph(&G);
DFS_Recurison_Traverse(&G);
printf("\n");
DFS_Traverse(&G);
printf("\n");
BFS_Traverse(&G);
printf("\n");
return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值