在讲邻接表之前,我觉得有必要介绍一下图的遍历操作:
深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次
广度优先遍历:系统地展开并遍历图中的所有节点,而且每个节点只能访问一次,与队列配合进行
关于邻接表这种存储结构,在数据结构里非常特殊,因为它是由顺序+链式存储,非单一的顺序存储或链式存储,所以相对来说也比较复杂;具象化一下描述:比如食堂打饭,每个窗口就是一个顶点,每个窗口前面排着的人就是要到这个窗口打饭的人,即表示人和窗口的关系,但是各个窗口前的人互不干涉,因此彼此之间不能直接访问;又比如你去坐高铁,你必须找到对应的站台号下去,否则下去之后发现站台不对,跟你要去的站台隔着几条铁轨,还不能直接过去,必须回到上面的大走廊再继续找。废话有点多了,下面来定义结构体以及相关操作:
边
//边
typedef struct Edge
{
int index;//顶点下标
struct Edge *next;//指向下一条边的地址
}Edge;
创建到 index 顶点的边
//创建到index顶点的边
Edge *create_edge(int index)
{
Edge *edge=malloc(sizeof(Edge));
edge->index=index;
edge->next=NULL;
return edge;
}
顶点
//顶点
typedef struct Vertex
{
char vertex;//顶点数据
Edge *first;//指向第一条边的指针
}Vertex;
图
//图
typedef struct graph
{
Vertex *v;//顶点数组
int cnt;//顶点数量
}Graph;
创建图
//创建图
Graph *create_graph(const char *str)
{
//申请图所需要的内存
Graph *graph=malloc(sizeof(Graph));
//计算顶点的数量
graph->cnt=strlen(str);
//申请存储顶点所需内存
graph->v=malloc(sizeof(Vertex)*graph->cnt);
//初始化顶点
for(int i=0;i<graph->cnt;i++)
{
graph->v[i].vertex=str[i];
graph->v[i].first=NULL;
}
return graph;
}
添加边
//添加边
bool add_graph(Graph *graph,char v1,char v2)
{
for(int i=0;i<graph->cnt;i++)
{
if(v1==graph->v[i].vertex)
{
for(int j=0;j<graph->cnt;j++)
{
if(v2==graph->v[j].vertex)
{
Edge *edge=create_edge(j);
edge->next=graph->v[i].first;
graph->v[i].first=edge;
return true;
}
}
}
}
return false;
}
入度
//入度
int id_graph(Graph *graph,char v)
{
int x=-1,num=0;
for(int i=0;i<graph->cnt;i++)
{
if(v==graph->v[i].vertex)
{
x=i;
break;
}
}
if(x==-1) return -1;
for(int i=0;i<graph->cnt;i++)
{
for(Edge *edge=graph->v[i].first;edge;edge=edge->next)
{
if(edge->index==x)
num++;
}
}
return num;
}
出度
//出度
int od_graph(Graph *graph,char v)
{
int num=0;
for(int i=0;i<graph->cnt;i++)
{
if(v==graph->v[i].vertex)
{
for(Edge *edge=graph->v[i].first;edge;edge=edge->next)
{
num++;
}
return num;
}
}
return -1;
}
显示图
//显示图
void show_graph(Graph *graph)
{
for(int i=0;i<graph->cnt;i++)
{
printf("index:%d v:%c ",i,graph->v[i].vertex);
for(Edge *edge=graph->v[i].first;edge;edge=edge->next)
{
printf("e:%d ",edge->index);
}
printf("\n");
}
}
深度优先遍历
//深度优先遍历
bool visit[7];
void _dfs_graph(Graph *graph,int i)
{
visit[i]=true;
printf("%c ",graph->v[i].vertex);
for(Edge *e=graph->v[i].first;e;e=e->next)
{
if(!visit[e->index])
_dfs_graph(graph,e->index);
}
}
void dfs_graph(Graph *graph)
{
_dfs_graph(graph,0);
}
广度优先遍历
//广度优先遍历
void bfs_graph(Graph *graph)
{
bool visited[graph->cnt];
ListQueue *queue=create_queue();
for(int i=0;i<graph->cnt;i++)
{
if(!visited[i])
{
visited[i]=true;
in_queue(queue,i);
}
while(queue->cnt)
{
int current=0;
head_queue(queue,¤t);
printf("%c ",graph->v[current].vertex);
out_queue(queue);
for(Edge *e=graph->v[current].first;e;e=e->next)
{
if(!visited[e->index])
{
visited[e->index]=true;
in_queue(queue,e->index);
}
}
}
}
destroy_queue(queue);
}
浅浅的测试一下:
int main(int argc,const char* argv[])
{
char *str="ABCDEFG";
Graph *graph=create_graph(str);
add_graph(graph,'A','B');
add_graph(graph,'A','D');
add_graph(graph,'B','D');
add_graph(graph,'B','E');
add_graph(graph,'C','D');
add_graph(graph,'C','F');
add_graph(graph,'D','C');
add_graph(graph,'D','G');
add_graph(graph,'E','G');
add_graph(graph,'F','G');
add_graph(graph,'G','F');
show_graph(graph);
printf("id=%d\n",id_graph(graph,'D'));
printf("od=%d\n",od_graph(graph,'E'));
dfs_graph(graph);
printf("\n");
bfs_graph(graph);
return 0;
}
over