简体   繁体   English

优先级队列实现为单链接无法更新插入时的指针引用

[英]Priority Queue implemented as singly linked failing to update pointer refrence on insert

Question Edited: How can I insert into my linked list while keeping pointer references as arguments? 问题编辑:如何在将指针引用保留为参数的同时插入到我的链表中? My updated code is as follows: 我更新的代码如下:

Note: I have a copy of the queue which was used in inserting a given node into the correct spot however I'm not seeing anyway to update the original queue as there no way to link the previous nodes. 注意:我有一个队列的副本,该副本曾用于将给定节点插入正确的位置,但是无论如何我都看不到要更新原始队列,因为无法链接先前的节点。

Resolved: Working priority queue (FIFO) - Needs cleaning 解决:工作优先级队列(FIFO)-需要清理

#define QUEUE_HEAD_INDICATOR 0.0

namespace
{
    pq* create_node(float priority = 0, const string & text = "")
    {
        pq* q = new pq;
        q->priority = priority;
        q->text = text;
        q->next = nullptr;
        return q;
    }
}

pq* init_priority_queue() {
    return create_node(QUEUE_HEAD_INDICATOR);
}


void insert(pq* &queue, string text, float priority) {
    if (!queue) return;

    pq* prev = queue;
    pq* cursor = queue->next;
    int offset = 0;

    if(prev->priority == 0.0 && prev->text == "") {
        prev->priority = priority;
        prev->text = text;
        return;
    }
    if(!cursor) {
        if(prev->priority > priority) {
            pq* node = create_node(priority, text);
            node->next = prev;
            prev = node;
        } else {
            pq* node = create_node(priority, text);
            prev->next = node;
        }
    } else {
        while(cursor && cursor->priority < priority) {
            prev = cursor;
            cursor = cursor->next;
            offset++;
        }
        pq* node = create_node(priority, text);
        if(offset == 0) {
            if(cursor->priority == (int)node->priority) {
                node->next = prev->next;
                prev->next = node;
                queue = prev;
            } else {
                node->next = prev;
                prev = node;
                queue = prev;
            }
        } else if(!cursor) {
            prev->next = node;
        } else {
            node->next = prev->next;
            prev->next = node;
        }
        return;
    }
}


string remove(pq* &queue) {
    pq* prev = queue;
    pq* cursor = queue->next;

    if(!queue->next) {
        string text = queue->text;
        prev = NULL;
        return text;
    }

    while(cursor->next) {
        prev = cursor;
        cursor = cursor->next;
    }

    prev->next = NULL;
    string text = cursor->text;
    return text;
}

Here is what the struct looks like 这是结构的样子

struct pq {
    float priority;
    string text;
    pq* next;
};

The assignment queue = new_node assigns the argument of the insert function, but not the pointer in the middle of the linked list (which is the next member variable of the previous element). 分配queue = new_node分配插入函数的参数,但不分配链接列表中间的指针(该指针是上next元素的next成员变量)。

    pq* qp = new pq;
    insert(qp, "0.1", 0.1f);
    // qp -> 0.1
    insert(qp, "0.3", 0.3f);
    // qp -> 0.1 -> 0.3
    insert(qp, "0.2", 0.2f);
    // qp -> 0.2 -> 0.3
    // qp now points to the 0.2 element, leaving the 0.1 element inaccessible

Also your function never compares the priority of the first element with the priority of the element to be inserted for queues with a length > 1. Your while loop only compares the priority of the element to be inserted to the priorities of the elements beyond the first element. 同样,对于长度大于1的队列,您的函数也不会将第一个元素的优先级与要插入的元素的优先级进行比较。while循环仅将要插入的元素的优先级与第一个元素之后的元素的优先级进行比较。元件。

I need to make an assumption here: queue is a reference to a pointer to the head node of a linked list. 我需要在这里做一个假设: queue是对指向链表头节点的指针的引用。 This seems a reasonable assumption because of the way it is used in the if(queue->next == NULL) case. 由于在if(queue->next == NULL)情况下使用它的方式,这似乎是一个合理的假设。

The following code will overwrite the pointer to the head node, and subsequently every other node, leaking it. 以下代码将覆盖指向头节点的指针,并随后覆盖每个其他节点的指针。

while(queue->next && queue->next->key < priority) {
    queue = queue->next; // bam! previous node leaked back at the caller
}

You could use a copy of the head node, but... There are much better ways to handle this. 您可以使用头节点的副本,但是...有更好的方法来处理此问题。

My recommendation is to not pass pointer to a root node. 我的建议是不要将指针传递到根节点。 Pass a pointer to the pointer to it. 将指针传递给指向它的指针。 This handles the head case by making the head look exactly like every other node and eliminates most of the code. 这通过使头部看起来与其他节点完全一样来处理头部情况,并消除了大多数代码。 Since we're always keeping a pointer to the previous node's next we have easy access to the insertion point for a new node and the next node. 由于我们始终保持一个指向前一节点的next ,我们可以很方便地将插入点为一个新的节点和下一个节点。

You cannot do this trick with a reference because a reference cannot be reassigned. 您不能使用引用来完成此技巧,因为无法重新分配引用。

Example: 例:

#include <string>
#include <iostream>

// my best guess at what pq looks like
struct pq
{
    pq* next;
    std::string text;
    float key;
};

void insert(pq ** queue, // cannot reseat ref, so pointer required
            const std::string &text, // ref eliminates copy.
                                     // const because we don't want to change
            float priority) {
    while((*queue) && // search while more queues
            (*queue)->key < priority) // and priority is low
    {
        queue = &(*queue)->next;
    }
    *queue = new pq{*queue, // if queue is null, end of list, if not inserted in middle
                    text, 
                    priority}; 
}

// Demo
int main()
{
    pq * queue = NULL;

    insert(&queue, "c", 5);
    insert(&queue, "a", 1);
    insert(&queue, "b", 2.5);
    insert(&queue, "e", 10);
    insert(&queue, "d", 7.5);

    // print and clean-up.
    while (queue)
    {
        std::cout << queue->text << std::endl;
        pq * temp = queue; // temp so we don't lose queue
        queue = queue->next;
        delete temp; // release node
    }
}

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

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