简体   繁体   English

使用BFS计算源和顶点之间的距离

[英]Using BFS to compute distance between a source and a vertex

I am trying to use adjacency list to compute the distance from a source vertex to the other vertices. 我正在尝试使用邻接表来计算从源顶点到其他顶点的距离。 I am using a queue to accomplish this however I get the distance of each vertex besides the source as -1, but I am not sure why this is happening 我正在使用一个队列来完成此操作,但是我得到除源之外每个顶点的距离为-1,但是我不确定为什么会这样

#include <stdio.h>
#include <stdlib.h>
#include "input_error.h"
#define VertexToSearch 1

typedef struct edge {
    int vertexIndex;
    struct edge *edgePtr;
} edge;

typedef struct vertex {
    int vertexKey;
    struct edge *edgePtr;
    int visited;
    int distance;
} vertex;

typedef struct queue {
    struct vertex v;
    struct queue* next;
}queue;

int vertexCount = 0;
struct vertex graph[];
void load_file(char*);
void insertEdge(int, int, struct vertex[]);
void InsertVertex(int, struct vertex[]);
void printGraph();
void bfs();
void print_distances();
queue* enqueue(queue*,vertex );
vertex dequeue(queue*);

enum error program_error;
int count;

int main(int argc, char** argv) {
    load_file(argv[1]);
    printGraph();
    bfs();
    print_distances();
    return 0;
}

void load_file(char* filename) {

    int vertex1;
    int vertex2;
    FILE* file = fopen(filename, "r");

    if (file == NULL) {
        printf("%s did not open\n", filename);
        program_error = FILE_FAILED_TO_OPEN;
        exit(program_error);
    }

    fscanf(file, "%d", &count);
    graph[count];
    for (int i = 0; i < count; i++) {
        InsertVertex(i + 1, graph);
    }
    for (int i = 0; i < count; i++) {
        fscanf(file, "\n(%d,%d)", &vertex1, &vertex2);
        insertEdge(vertex1, vertex2, graph);
    }
    fclose(file);
}

void InsertVertex(int vertexKey, struct vertex graph[]) {
    graph[vertexCount].vertexKey = vertexKey;
    graph[vertexCount].edgePtr = NULL;
    graph[vertexCount].visited = 0;
    graph[vertexCount].distance = -1;
    vertexCount++;
}

void insertEdge(int vertex1, int vertex2, struct vertex graph[]) {
    struct edge *e, *e1, *e2;
    e = graph[vertex1 - 1].edgePtr;
    while (e && e->edgePtr) {
        e = e->edgePtr;
    }
    e1 = (struct edge *) malloc(sizeof (*e1));
    e1->vertexIndex = vertex2;
    e1->edgePtr = NULL;
    if (e)
        e->edgePtr = e1;
    else
        graph[vertex1 - 1].edgePtr = e1;

    e = graph[vertex2 - 1].edgePtr;
    while (e && e->edgePtr) {
        e = e->edgePtr;
    }
    e2 = (struct edge *) malloc(sizeof (*e2));
    e2->vertexIndex = vertex1;
    e2->edgePtr = NULL;
    if (e)
        e->edgePtr = e2;
    else
        graph[vertex2 - 1].edgePtr = e2;
}

void printGraph() {
    int i;
    struct edge *e;
    for (i = 0; i < vertexCount; i++) {
        printf("%d(%d)", i + 1, graph[i].vertexKey);
        e = graph[i].edgePtr;
        while (e) {
            printf("->%d", e->vertexIndex);
            e = e->edgePtr;
        }
        printf("\n");
    }
}

void bfs() {
    graph[0].distance = 0;
    queue* q = NULL;
   q = enqueue(q,graph[0]);
    while(q->next != NULL){
        vertex u = dequeue(q);
        while(u.edgePtr != NULL){
           if(graph[u.edgePtr->vertexIndex -1 ].distance == -1){
              graph[u.edgePtr->vertexIndex -1 ].distance = u.distance + 1;
              enqueue(q, graph[u.edgePtr->vertexIndex -1 ]);
           } 
           u.edgePtr = u.edgePtr->edgePtr;
        }
    }

}

void print_distances() {
    for (int i = 0; i < count; i++) {
        printf("%d %d\n", i + 1, graph[i].distance);
    }
}

queue* enqueue(queue* q,vertex v) {
    queue* new = malloc(sizeof (queue));
    new->next = NULL; 
    new->v = v;
    if (q == NULL) {
        q = malloc(sizeof(queue));
        q = new;
    } else {
        while (q->next != NULL) {
            q = q->next;
        }
        //add new node at the end
        q->next = new;

    }
    return q;
}

vertex dequeue(queue* q) {
    vertex v;
    queue* tempPtr;
    tempPtr = q; //makes temp the address of the node to be deleted
    v = tempPtr->v;
    q = q->next; //sets the new head as the address of the next node

    return v;
} 

I have figured it out, basically my queue implementation was horrible and dequeue was not clearing out the queue, also this while(q->next != NULL) was incorrect it should be while(q != NULL) Below is the correct implementation of this program 我已经弄清楚了,基本上我的队列实现很糟糕并且出队没有清除队列,这while(q->next != NULL)是不正确的, while(q != NULL)下面是正确的实现该程序的

#include <stdio.h>
#include <stdlib.h>
#include "input_error.h"
#define VertexToSearch 1

typedef struct edge {
    int vertexIndex;
    struct edge *edgePtr;
} edge;

typedef struct vertex {
    int vertexKey;
    struct edge *edgePtr;
    int visited;
    int distance;
} vertex;

typedef struct queue {
    struct vertex v;
    struct queue* next;
}queue;

int vertexCount = 0;
struct vertex graph[];
queue* q = NULL;
void load_file(char*);
void insertEdge(int, int, struct vertex[]);
void InsertVertex(int, struct vertex[]);
void printGraph();
void bfs();
void print_distances();
void enqueue(vertex);
vertex dequeue();

enum error program_error;
int count;

int main(int argc, char** argv) {
    load_file(argv[1]);
    printGraph();
    bfs();
    print_distances();
    return 0;
}

void load_file(char* filename) {

    int vertex1;
    int vertex2;
    FILE* file = fopen(filename, "r");

    if (file == NULL) {
        printf("%s did not open\n", filename);
        program_error = FILE_FAILED_TO_OPEN;
        exit(program_error);
    }

    fscanf(file, "%d", &count);
    graph[count];
    for (int i = 0; i < count; i++) {
        InsertVertex(i + 1, graph);
    }
    for (int i = 0; i < count; i++) {
        fscanf(file, "\n(%d,%d)", &vertex1, &vertex2);
        insertEdge(vertex1, vertex2, graph);
    }
    fclose(file);
}

void InsertVertex(int vertexKey, struct vertex graph[]) {
    graph[vertexCount].vertexKey = vertexKey;
    graph[vertexCount].edgePtr = NULL;
    graph[vertexCount].visited = 0;
    graph[vertexCount].distance = -1;
    vertexCount++;
}

void insertEdge(int vertex1, int vertex2, struct vertex graph[]) {
    struct edge *e, *e1, *e2;
    e = graph[vertex1 - 1].edgePtr;
    while (e && e->edgePtr) {
        e = e->edgePtr;
    }
    e1 = (struct edge *) malloc(sizeof (*e1));
    e1->vertexIndex = vertex2;
    e1->edgePtr = NULL;
    if (e)
        e->edgePtr = e1;
    else
        graph[vertex1 - 1].edgePtr = e1;

    e = graph[vertex2 - 1].edgePtr;
    while (e && e->edgePtr) {
        e = e->edgePtr;
    }
    e2 = (struct edge *) malloc(sizeof (*e2));
    e2->vertexIndex = vertex1;
    e2->edgePtr = NULL;
    if (e)
        e->edgePtr = e2;
    else
        graph[vertex2 - 1].edgePtr = e2;
}

void printGraph() {
    int i;
    struct edge *e;
    for (i = 0; i < vertexCount; i++) {
        printf("%d(%d)", i + 1, graph[i].vertexKey);
        e = graph[i].edgePtr;
        while (e) {
            printf("->%d", e->vertexIndex);
            e = e->edgePtr;
        }
        printf("\n");
    }
}

void bfs() {
    graph[0].distance = 0;
   enqueue(graph[0]);
    while(q != NULL){
        vertex u = dequeue();
        while(u.edgePtr != NULL){
       if(graph[u.edgePtr->vertexIndex - 1].distance == -1){
          graph[u.edgePtr->vertexIndex - 1].distance = u.distance + 1;
          enqueue(graph[u.edgePtr->vertexIndex - 1]);
       } 
           u.edgePtr = u.edgePtr->edgePtr;
        }
    }

}

void print_distances() {
    for (int i = 0; i < count; i++) {
        printf("%d %d\n", i + 1, graph[i].distance);
    }
}

void enqueue(vertex v) {
    queue* new = malloc(sizeof (queue));
    new->next = NULL; 
    new->v = v;
    if (q == NULL) {
        q = malloc(sizeof(queue));
        q = new;
    } else {
        while (q->next != NULL) {
            q = q->next;
        }
        //add new node at the end
        q->next = new;

    }
}

vertex dequeue() {
    vertex v;
    queue* tempPtr;
    tempPtr = q; //makes temp the address of the node to be deleted
    v = tempPtr->v;
    q = q->next; //sets the new head as the address of the next node
    return v;
}

In insertVertex(...) , you call graph[vertexCount].distance = -1; insertVertex(...) ,您调用graph[vertexCount].distance = -1; .

It is very likely that your code isn't changing distance properly. 您的代码很可能没有正确更改距离。 From what I can see, you set u.edgePtr->vertexIndex to the index of the second vertex connected - 1 (in insertEdge(...) ). 从我所看到的,您将u.edgePtr->vertexIndex设置为连接的第二个顶点的索引-1 (在insertEdge(...) )。 This means you're probably converting from human-readable indexes (1, 2, ... n) into machine-readable indexes (0, 1, ... n-1) 这意味着您可能正在将人类可读的索引(1、2,... n)转换为机器可读的索引(0、1,... n-1)

In bfs() you shouldn't need to do this a second time! bfs()您不需要第二次! I couldn't find any reason to set graph[u.edgePtr->vertexIndex - 1].distance , but I could be mistaken. 我找不到设置graph[u.edgePtr->vertexIndex - 1].distance任何理由,但我可能会误解。 I've redone your while loop. 我已重做您的while循环。 Try putting this in bfs() : 尝试将其放在bfs()

while(u.edgePtr != NULL){
       if(graph[u.edgePtr->vertexIndex].distance == -1){
          graph[u.edgePtr->vertexIndex].distance = u.distance + 1;
          enqueue(q, graph[u.edgePtr->vertexIndex]);
       } 

I'm not sure why none of your distances are changing, because your code should still be affecting the distances at index-1 just fine. 我不确定为什么您的距离都没有变化,因为您的代码仍然应该影响index-1处的距离。 Try the fix above and let me know if that was enough to catch the bug or if there might be another one. 尝试上面的修复,让我知道这是否足以捕获该错误,或​​者是否还有另一个错误。

You have the general idea. 您有大致的想法。 Here are some ways to simplify the code 以下是一些简化代码的方法

  • Use an array based queue with fixed size. 使用固定大小的基于数组的队列。 The size 256 is ideal since the unsigned integer indexes will roll over automatically. 大小256是理想的,因为无符号整数索引将自动翻转。 Trade-off: simple code but no more than 255 items in the queue at any given time. 权衡:简单的代码,但在任何给定时间队列中最多包含255个项目。
  • Use an array of edges. 使用边缘阵列。 Trade-off: easy to implement the edge array, but takes an O(E) search to find the edges that originate from any given vertex. 折衷:易于实现边缘阵列,但需要进行O(E)搜索以找到源自任何给定顶点的边缘。
  • Use the distance to keep track of whether a node has been visited. 使用距离来跟踪是否已访问节点。 A negative distance means that the vertex has not been visited. 负距离表示尚未访问顶点。 Trade-off: seems like a win-win to me, simpler code, less space, no extra time. 权衡:对我来说似乎是双赢,更简单的代码,更少的空间,没有多余的时间。
  • Use the vertex ID to locate a vertex in the vertex array. 使用顶点ID在顶点数组中定位一个顶点。 Trade-off: prevents a malformed edge from crashing your program, but requires an O(V) search to find the vertex. 权衡:防止畸形的边缘使程序崩溃,但需要进行O(V)搜索以找到顶点。

Here's the simplified code. 这是简化的代码。 I leave it as an exercise for the reader to optimize the code for speed and/or remove the queue limit, as desired. 我把它留给读者作为练习,以根据需要优化代码的速度和/或删除队列限制。

#include <stdio.h>
#include <stdint.h>

struct Vertex
{
    int id;
    int distance;
};

struct Queue
{
    uint8_t head;
    uint8_t tail;
    void *data[256];
};

int main( void )
{
    int edge[][2] = { {2,3}, {1,4}, {1,3}, {3,4}, {4,5}, {0,0} };
    struct Vertex vertex[] = { {1,0}, {2,-1}, {3,-1}, {4,-1}, {5,-1}, {0,0} };
    struct Queue q = { 0, 0 };

    q.data[q.head++] = &vertex[0];
    while ( q.tail != q.head )
    {
        struct Vertex *src = q.data[q.tail++];
        for ( int i = 0; edge[i][0] > 0; i++ )
            for ( int j = 0; j < 2; j++ )
                if ( edge[i][j] == src->id )
                {
                    int destID = edge[i][(j+1)%2];
                    struct Vertex *dest;
                    for ( dest = vertex; dest->id > 0; dest++ )
                        if ( dest->id == destID )
                            break;

                    if ( dest->distance < 0 )
                    {
                        dest->distance = src->distance + 1;
                        q.data[q.head++] = dest;
                    }
                }
    }

    for ( int i = 0; vertex[i].id > 0; i++ )
        printf( "Vertex %d is at distance %d\n", vertex[i].id, vertex[i].distance );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM