【问题标题】:Using BFS to compute distance between a source and a vertex使用 BFS 计算源和顶点之间的距离
【发布时间】:2016-03-10 03:47:06
【问题描述】:

我正在尝试使用邻接表来计算从源顶点到其他顶点的距离。我正在使用队列来完成此操作,但是我将除源之外的每个顶点的距离设为 -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;
} 

【问题讨论】:

    标签: c queue breadth-first-search


    【解决方案1】:

    我想通了,基本上我的队列实现很糟糕,出队没有清除队列,而且这个while(q-&gt;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;
    }
    

    【讨论】:

    • 干得好!调试是一项有用的技能,它总是会让你忙几个小时的小错误。我开始怀疑你的队列是否有问题,因为这是我唯一没有批判性地讨论过的事情,因为我认为它是正确的。
    • @Aaron3468: 是的,我一开始并没有考虑到这一点,我使用了一个用于不同程序的队列并更改了变量名,但忘记添加一些东西来使它工作这个项目
    【解决方案2】:

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

    您的代码很可能没有正确更改距离。据我所知,您将u.edgePtr-&gt;vertexIndex 设置为连接的第二个顶点的索引- 1(在insertEdge(...) 中)。这意味着您可能正在从人类可读的索引 (1, 2, ... n) 转换为机器可读的索引 (0, 1, ... n-1)

    bfs() 中,您不需要再次执行此操作!我找不到任何理由设置graph[u.edgePtr-&gt;vertexIndex - 1].distance,但我可能弄错了。我已经重做了你的 while 循环。试试把这个放到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]);
           } 
    

    我不确定为什么 none 您的距离会发生变化,因为您的代码应该仍然会影响 index-1 的距离就好了。尝试上面的修复,让我知道这是否足以捕获错误,或者是否可能存在另一个错误。

    【讨论】:

    • 仍然得到所有 -1
    【解决方案3】:

    你有大致的想法。这里有一些简化代码的方法

    • 使用固定大小的基于数组的队列。尺寸 256 是理想的,因为 无符号整数索引将自动翻转。权衡: 简单的代码,但在任何给定的队列中不超过 255 个项目 时间。
    • 使用边数组。权衡:易于实现边缘阵列, 但需要 O(E) 搜索来找到源自任何 给定顶点。
    • 使用距离来跟踪节点是否已被访问。一种 负距离表示该顶点尚未被访问。 权衡:对我来说似乎是双赢的,更简单的代码,更少的空间,没有 额外的时间。
    • 使用顶点 ID 在顶点数组中定位一个顶点。权衡: 防止格式错误的边缘使您的程序崩溃,但需要 O(V) 搜索找到顶点。

    这是简化的代码。我将其作为练习留给读者根据需要优化代码以提高速度和/或删除队列限制。

    #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 );
    }
    

    【讨论】:

    • 我可以看到它是多么简单,但是最大顶点数以 UINT_MAX 的形式给出,我必须让它是动态的,因为它是从输入文件中读取的
    • @BrandonTomblinson 是的,我已经为您提供了一个工作起点。您可以逐步将其演变为您想要的解决方案。例如,用基于链表的队列替换队列代码,并确保它仍然有效。然后将边列表添加到顶点以加快搜索边等。当您使用静态数据处理完所有内容后,然后添加代码以从文件中读取数据。
    • 我明白了,但是没有简单的方法可以将您的距离算法移植到我当前的顶点和边配置中吗?
    猜你喜欢
    • 2014-11-29
    • 1970-01-01
    • 2021-01-02
    • 2014-02-28
    • 2010-10-30
    • 1970-01-01
    • 2014-01-28
    相关资源
    最近更新 更多