【问题标题】:competitve programming Dijkstra竞技编程 Dijkstra
【发布时间】:2018-11-13 10:20:39
【问题描述】:

谁能告诉我这个程序的错误在哪里,这真的很有帮助,我尽力解决这个问题,这段代码只通过了两个测试用例 给定一个无向图和一个起始节点,确定从起始节点到图中所有其他节点的最短路径的长度。如果一个节点不可达,它的距离是-1。节点将从 到 连续编号,并且边将具有不同的距离或长度。 这是问题https://www.hackerrank.com/challenges/dijkstrashortreach/problem

    #include <stdio.h> 
    #include <stdlib.h> 
    #pragma warning(disable:4996)
    // Node 
    typedef struct node {
        int data;

        // Lower values indicate higher priority 
        int priority;

        struct node* next;

    } Node;

    // Function to Create A New Node 
    Node* newNode(int d, int p)
    {
        Node* temp = (Node*)malloc(sizeof(Node));
        temp->data = d;
        temp->priority = p;
        temp->next = NULL;

        return temp;
    }

    // Return the value at head 
    int peek(Node** head)
    {
        return (*head)->data;
    }

    // Removes the element with the 
    // highest priority form the list 
    void pop(Node** head)
    {
        Node* temp = *head;
        (*head) = (*head)->next;
        free(temp);
    }
    void updateprt(Node** head, int data) {
        if ((*head)->data == data)
        {
            Node* temp = *head;
            *head = (*head)->next;
            free(temp);
            return;
        }
        Node* prev = *head;

        while ((prev->next)->data != data) {
            prev = prev->next;
        }
        Node* start = prev->next;
        prev->next = start->next;
        free(start);

    }

    // Function to push according to priority 
    void push(Node** head, int d, int p)
    {
        Node* start = (*head);

        // Create new Node 
        Node* temp = newNode(d, p);
        if (*head == NULL) {
            *head = temp;
            return;
        }
        // Special Case: The head of list has lesser 
        // priority than new node. So insert new 
        // node before head node and change head node. 
        if ((*head)->priority > p) {

            // Insert New Node before head 
            temp->next = *head;
            (*head) = temp;
        }
        else {

            // Traverse the list and find a 
            // position to insert new node 
            while (start->next != NULL &&
                start->next->priority < p) {
                start = start->next;

            }

            // Either at the ends of the list 
            // or at required position 
            temp->next = start->next;
            start->next = temp;
        }
    }

    // Function to check is list is empty 
    int isEmpty(Node** head)
    {
        return (*head) == NULL;
    }
    struct adjlistnode {
        int data;
        struct adjlistnode* next;
    };
    struct adjlist {
        struct adjlistnode* head;
    };
    struct graph {
        int v;
        struct adjlist* array;
    };
    struct graph* creategraph(int v) {
        struct graph* G = (struct graph*) malloc(sizeof(struct graph));
        G->v = v;
        int i;
        G->array = (struct adjlist*)malloc(sizeof(struct adjlist)*v);
        for (i = 0; i < v; i++) {
            G->array[i].head = NULL;
        }
        return G;
    }
    int  Distance[100000], path[50];
    struct adjlistnode* getnewnode(int ver) {
        struct adjlistnode* newnode = (struct adjlistnode*)malloc(sizeof(struct adjlistnode));
        newnode->data = ver;
        newnode->next = NULL;
        return newnode;

    }
    void addedge(struct graph* G, int src, int dest, long int w, long int** weight) {
        struct adjlistnode* temp;
        temp = getnewnode(dest);
        temp->next = G->array[src].head;
        G->array[src].head = temp;

        temp = getnewnode(src);
        temp->next = G->array[dest].head;
        G->array[dest].head = temp;
        if (weight[src][dest] != 0 || weight[dest][src] != 0 && w < weight[src][dest]) {
            weight[src][dest] = w;
            weight[dest][src] = w;
        }
        if (weight[src][dest] == 0) {
            weight[src][dest] = w;
            weight[dest][src] = w;
        }
    }
    void printgraph(struct graph* G) {
        for (int i = 0; i < G->v; i++) {
            struct adjlistnode* temp = G->array[i].head;
            printf("%d->   ", i);
            while (temp) {
                printf(" %d", temp->data);
                temp = temp->next;
            }
            printf("\n");
        }
    }

    void Dijkstra(Node** queue, struct graph* G, int s, long int** weight) {
        int v, w, d;
        push(queue, s, 0);
        for (int i = 0; i < 100000; i++) {
            Distance[i] = -1;
        }
        Distance[s] = 0;
        while (!isEmpty(queue)) {
            v = peek(queue);
            pop(queue);
            struct adjlistnode* temp = G->array[v].head;
            while (temp) {
                w = temp->data;
                d = Distance[v] + weight[v][w];

                //To update the distance of w check the below two conditions
                if (Distance[w] == -1) {
                    Distance[w] = d;
                    push(queue, w, d);
                    path[w] = v;
                }
                if (Distance[w] > d)
                {
                    Distance[w] = d;

                    path[w] = v;
                    updateprt(queue, w);
                    push(queue, w, d);


                }
                temp = temp->next;
            }
        }
    }


    int main()
    {
        int t;
        scanf("%d", &t);
        while (t) {

            Node* pq = NULL;

            int v;
            int e;
            scanf("%d %d", &v, &e);
            long int** weight = (long int**)malloc(sizeof(long int*)*v);
            for (int i = 0; i < v; i++)
                weight[i] = (long int*)malloc(sizeof(long int)*v);
            struct graph* G = creategraph(v);
            int u, w;
            long int l;
            for (int i = 0; i < e; i++) {
                scanf("%d %d %ld", &u, &w, &l);
                addedge(G, u - 1, w - 1, l, weight);
            }

            int s;
            scanf("%d", &s);
            //    printgraph(G);
                //printf("\n");
            Dijkstra(&pq, G, s - 1, weight);
            for (int i = 0; i < G->v; i++) {
                if (i == s - 1)
                    continue;
                printf("%d ", Distance[i]);
            }
            /*    while (!isEmpty(&pq)) {
                    printf("%d ", peek(&pq));
                    pop(&pq);
                }*/


            return 0;
        }
        system("pause");
    }

【问题讨论】:

  • 哇,这里有很多代码要发布。请进行一些调试并缩小问题范围。也许您应该从对优先级队列进行一些独立测试开始。例如,当在 updateprt 中找不到 data 时,您取消引用空指针。而且您永远不会正确使用指向节点指针的东西:如果您传递Node **,您希望能够通过指针修改调用函数中的队列头,但您从未真正这样做过。
  • 我现在投票决定关闭它。笨蛋,如果可以的话,编辑问题以包含minimal reproducible example 的所有部分。

标签: c algorithm data-structures


【解决方案1】:

您最大的问题是优先级队列,您将其实现为简单的链表。您似乎对何时以及如何使用指向节点指针的指针感到困惑。

例如这个函数:

int isEmpty(Node **head) ...

只检查列表。它没有修改它,所以传递一个节点指针就足够了:

int isEmpty(Node *head) ...

此函数也不会修改节点的内容,因此可以明确说明:

int isEmpty(const Node *head) ...

另一方面,pushpopupdateprt 函数必须能够通过指针更改头部,因此它们需要指向节点指针的指针。你的 pop 函数总是改变头部,它会这样做。其他函数如下所示:

void push(Node** head, int d, int p)
{
    Node* start = (*head);

    // ... do stuff with start, leave head alone ...
}

在这里,您刚刚使用指向节点指针的指针作为一种过于模糊的方式来传递有关头部的信息,但您从未修改它(插入到空列表中的特殊情况除外)。

push 函数的外观如下:

void push(Node **head, int d, int p)
{
    Node *temp = newNode(d, p);

    while (*head && (*head)->priority < p) {
        head = &(*head)->next;
    }

    temp->next = *head;
    *head = temp;
}

请注意没有任何特殊情况。调用push(&amp;queue, ...)时,传入的是头指针的地址,可以通过赋值给*head来修改调用函数中的局部变量queue。当您使用head = &amp;(*head)-&gt;next 遍历列表时,head 保存前一个节点的next 字段的地址,您也可以通过*head 修改该字段。指向指针的指针增加了一层间接性,并告诉您来自哪里并允许您修改该值。

同样,您可以更改(并简化)您的 updateptr 函数:

void updateprt(Node** head, int data)
{
    while (*head && (*head)->data != data) {
        head = &(*head)->next;
    }

    if (*head) pop(head);
}

通过这些更改,您的程序应该可以运行,但还有其他需要注意的事项:

if (weight[src][dest] != 0 || weight[dest][src] != 0 && w

条件(w1 == 0 || w2 == 0 &amp;&amp; w &lt; w1) 被解析为(w1 == 0 || (w2 == 0 &amp;&amp; w &lt; w1)),这可能不是您想要的。没有正确完成对零的检查,因为您从未初始化 weight。 (您可以使用calloc 而不是malloc 来创建一个零初始化数组。)`

但是为什么有一个单独的权重数组呢?您可以存储每个边缘的重量:

struct adjlistnode {
    int data;                   // destination vertex
    int weight;
    struct adjlistnode* next;
};

数组也是如此:

int  Distance[100000], path[50];

两个数组都为每个顶点提供一个条目。第一个太慷慨了(“我会多分配一点,以防万一……”),第二个可能太小了。 (您不需要任务的路径,但能够验证它总是很好的。变量不是路径,但是,它为每个顶点保持下一步的起点。)

竞争规则说最多有 3,000 个节点,因此您可以使用 [3000] 为数组标注维度,但您也可以将它们设为图结构的一部分并根据顶点数进行分配。

祝你好运!

【讨论】:

  • @M Oehm is my above updateptr 是正确的我正在删除与数据匹配的节点,然后我再次以相同的数据更新优先级推送
  • 没有。它有效吗?我不这么认为。我建议您编写一个小驱动程序main 来测试您的队列。我相信你会发现一些错误,你应该尝试通过调试来消除它们。
  • (对于它的价值,您添加了一个删除头部的特殊情况,但基本问题仍然存在:如果您查找的值不在列表中,您最终取消引用prev 为空时,通常会导致分段错误。另外,当你删除一个节点时,你必须调整之前的节点的next。你怎么知道那在哪里?你也可以使用代码我已经在上面向你展示了。你的实现结合了我上面的建议是here。)
  • 非常感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 2013-08-05
  • 2015-10-08
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多