优先级队列实现为单链接未能在插入时更新指针引用

Posted

技术标签:

【中文标题】优先级队列实现为单链接未能在插入时更新指针引用【英文标题】:Priority Queue implemented as singly linked failing to update pointer refrence on insert 【发布时间】:2019-03-17 15:13:42 【问题描述】:

问题已编辑:如何在将指针引用作为参数的同时插入到我的链表中?我的更新代码如下:

注意:我有一份队列副本,用于将给定节点插入正确的位置,但是我看不到更新原始队列,因为无法链接先前的节点。

已解决:工作优先级队列 (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;

这是结构的样子

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

【问题讨论】:

为什么要用 C++ 编写 C 代码? @Yashasn 我不明白为什么它会很重要,因为它符合要求但在中间插入时会失败。 【参考方案1】:

我需要在这里做一个假设:queue 是对指向链表头节点的指针的引用。这似乎是一个合理的假设,因为它在 if(queue-&gt;next == NULL) 案例中的使用方式。

以下代码将覆盖指向头节点的指针,随后覆盖所有其他节点,泄漏它。

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

您可以使用头节点的副本,但是...有更好的方法来处理这个问题。

我的建议是不要将指针传递给根节点。将指针传递给指向它的指针。这通过使头部看起来与其他所有节点完全相同来处理头部情况并消除大部分代码。由于我们始终保留指向前一个节点的next 的指针,因此我们可以轻松访问新节点和下一个节点的插入点。

你不能用引用做这个技巧,因为引用不能被重新分配。

例子:

#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
    

【讨论】:

@smerlin 这帮助很大!我不知道你不能辞职参考。所以如果我想保持相同的队列结构,我必须做一些事情,比如创建一个新函数,在 queue = queue->next 之前保存每个指针引用 @usuer4581301 ^ @Garrett 我上面描述的内容应该适用于您的队列结构,而不需要辅助函数,但在您的特定情况下我可能会遗漏一些东西。考虑提出一个新问题,这样您就可以提供一个更完整的例子来说明您目前的情况。 我更新了主要问题。在这种特定情况下,我实际上必须使用指针引用。我能够隔离创建链接列表副本并将新节点插入正确位置的问题。现在我的原始队列未修改,但是我仍然没有解决在不覆盖队列中先前节点的情况下将列表的其余部分放在正确位置的问题。 您不想创建新队列。您陷入了头节点的特殊情况。太糟糕了。你永远不想要特殊情况,即使你无法避免并且不得不咬紧牙关忍受它。稍后我将不得不回到这个问题。【参考方案2】:

queue = new_node的赋值赋值是insert函数的参数,而不是链表中间的指针(也就是前一个元素的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

此外,您的函数永远不会将第一个元素的优先级与要插入的元素的优先级进行比较,以用于长度 > 1 的队列。 您的 while 循环仅将要插入的元素的优先级与第一个元素之外的元素的优先级进行比较。

【讨论】:

以上是关于优先级队列实现为单链接未能在插入时更新指针引用的主要内容,如果未能解决你的问题,请参考以下文章

优先级队列

TencentOS-tiny中队列环形队列优先级队列的实现及使用

优先队列与堆

索引优先队列的工作原理与简易实现

如何实现 numba jited 优先级队列?

C++ 对象指针的优先级队列在运行时错误为无效堆