指向节点的双指针

时间:2018-11-16 18:10:31

标签: c pointers memory-management

我的疑问仅是关于指针,如果我们使用Queue,则头部是指向*head的双指针,而不是访问main内部的位置(或传递的地址),但是当我们仅使用指针时head比我们仅在当前函数中使用标题要容易,Queue会保留指向head=&(*head)->next的指针的地址,因为(* head)-> next本身一个地址,当我们在此之前使用&时,将创建一个单独的块,然后将创建存储块并保存(* head)-> next的地址,并将该地址分配给head 我对此表示怀疑,因为它就像一个两步过程,不能直接将(* head)-> next放在head内,所以我们需要传递地址的地址,这将需要一个额外的块,并且当循环将执行n次,而不是n次中间块? 请告诉我我是否正确 并说出正确的逻辑,谢谢

void queue_push(Queue **head, int d, int p)
{
    Queue *q = queue_new(d, p);

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

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

完整程序是

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>

    typedef struct Queue Queue;

    struct Queue {
        int data;
        int priority;
        Queue *next;
    };

    Queue *queue_new(int d, int p)
    {
        Queue *n = malloc(sizeof(*n));

        n->data = d;
        n->priority = p;
        n->next = NULL;

        return n;
    }

    int queue_pop(Queue **head)
    {
        assert(*head);

        Queue *old = *head;
        int res = old->data;

        *head = (*head)->next;
        free(old);

        return res;
    }

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

        if (*head) queue_pop(head);
    }

    void queue_push(Queue **head, int d, int p)
    {
        Queue *q = queue_new(d, p);

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

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


    int queue_empty(Queue *head)
    {
        return (head == NULL);
    }

    void queue_print(const Queue *q)
    {
        while (q) {
            printf("%d[%d] ", q->data, q->priority);
            q = q->next;
        }

        puts("$");
    }

    typedef struct Graph Graph;
    typedef struct Edge Edge;

    struct Edge {
        int vertex;
        int weight;
        Edge *next;
    };

    struct Graph {
        int v;
        Edge **edge;
        int *dist;
        int *path;
    };

    Graph *graph_new(int v)
    {
        Graph *G = malloc(sizeof(*G));

        G->v = v;
        G->edge = calloc(v, sizeof(*G->edge));
        G->dist = calloc(v, sizeof(*G->dist));
        G->path = calloc(v, sizeof(*G->path));

        return G;
    }

    void graph_delete(Graph *G)
    {
        if (G) {
            for (int i = 0; i < G->v; i++) {
                Edge *e = G->edge[i];

                while (e) {
                    Edge *old = e;

                    e = e->next;
                    free(old);
                }
            }

            free(G->edge);
            free(G->dist);
            free(G->path);
            free(G);
        }
    }

    Edge *edge_new(int vertex, int weight, Edge *next)
    {
        Edge *e = malloc(sizeof(*e));

        e->vertex = vertex;
        e->weight = weight;
        e->next = next;

        return e;
    }

    void graph_edge(Graph *G, int u, int v, int w)
    {
        G->edge[u] = edge_new(v, w, G->edge[u]);
        G->edge[v] = edge_new(u, w, G->edge[v]);
    }

    void dijkstra(const Graph *G, int s)
    {
        Queue *queue = NULL;

        for (int i = 0; i < G->v; i++) G->dist[i] = -1;
        G->dist[s] = 0;

        queue_push(&queue, s, 0);

        while (!queue_empty(queue)) {
            int v = queue_pop(&queue);
            Edge *e = G->edge[v];

            while (e) {
                int w = e->vertex;
                int d = G->dist[v] + e->weight;

                if (G->dist[w] == -1) {
                    G->dist[w] = d;
                    G->path[w] = v;

                    queue_push(&queue, w, d);
                }

                if (G->dist[w] > d) {
                    G->dist[w] = d;
                    G->path[w] = v;

                    queue_remove(&queue, w);
                    queue_push(&queue, w, d);
                }

                e = e->next;
            }
        }
    }

    int main()
    {
        int t;

        scanf("%d", &t);

        while (t--) {
            Graph *G;
            int v, e, s;

            scanf("%d %d", &v, &e);

            G = graph_new(v);

            for (int i = 0; i < e; i++) {
                int u, v, w;

                scanf("%d %d %d", &u, &v, &w);
                graph_edge(G, u - 1, v - 1, w);
            }

            scanf("%d", &s);
            dijkstra(G, s - 1);

            for (int i = 0; i < G->v; i++) {
                if (i != s - 1) {
                    printf("%d ", G->dist[i]);
                }
            }

            puts("");
            graph_delete(G);
        }

        return 0;
    }

1 个答案:

答案 0 :(得分:0)

queue_push()无法正确插入新节点。

仅一个节点的.next被更新。通常,两个节点需要更新其.next成员。原始列表中的一个(在新节点的位置之前)和新列表中的一个。


我发现在列表superhead之前创建一个临时节点可以简化代码。

void queue_push(Queue **head, int d, int p) {
  Queue *q = queue_new(d, p);

  Queue superhead; // Only superhead.next member important.
  superhead.next = *head;  
  Queue *previous = &superhead;

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

  q->next = previous->next;
  previous->next = q;
  *head = superhead.next;
}