You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于最小堆实现的自定义优先级队列出现异常行为

最小堆实现优先级队列的问题排查与修复

我用C++基于最小堆实现了一个优先级队列,采用向量零索引(索引i的左右子节点为2i+1和2i+2),使用递归的swim和sink函数。实现代码如下:

#include <iostream>
#include <vector>

using namespace std;

template <class t>
class priorityqueue
{
private:
    vector<t> vec;

    inline static void swap(t *x, t *y)
    {
        t temp = *x;
        *x = *y;
        *y = temp;
    }

    inline static size_t left(size_t i) { return (i << 1) + 1; }

    inline static size_t right(size_t i) { return (i << 1) + 2; }

    inline static size_t up(size_t i) { return (i - 1) >> 1; }

    inline bool validindex(size_t i) { return i < vec.size(); }

    inline void swim(size_t i)
    {
        if (i == 0)
            return;
        size_t up_ = up(i);
        if (vec[i] < vec[up_])
        {
            swap(&vec[i], &vec[up_]);
            swim(up_);
        }
    }

    inline void sink(size_t i)
    {
        size_t left_ = left(i), right_ = right(i);
        if (!validindex(left_))
        {
            return;
        }
        if (!validindex(right_))
        {
            if (vec[i] > vec[left_])
            {
                swap(&vec[i], &vec[left_]);
                sink(left_);
            }
            return;
        }
        if (vec[i] < vec[left_])
        {
            if (vec[i] < vec[right_])
            {
                return;
            }
            swap(&vec[i], &vec[right_]);
            sink(right_);
        }
        else
        {
            if (vec[i] < vec[right_])
            {
                swap(&vec[i], &vec[left_]);
                sink(left_);
            }
            else
            {
                if (vec[left_] < vec[right_])
                {
                    swap(&vec[i], &vec[left_]);
                    sink(left_);
                }
                else
                {
                    swap(&vec[i], &vec[right_]);
                    sink(right_);
                }
            }
        }
    }

public:
    size_t size() { return vec.size(); }

    bool empty() { return size() == 0; }

    void push(t elem)
    {
        vec.push_back(elem);
        swim(vec.size() - 1);
    }

    t peek()
    {
        return vec[0];
    }

    t pop()
    {
        t elem = vec[0];
        t last = vec[vec.size() - 1];
        swap(&last, &elem);
        vec.pop_back();
        sink(0);
        return elem;
    }
};

int main()
{
    priorityqueue<int> pq;
    pq.push(3);
    pq.push(1);
    pq.push(4);
    pq.push(1);
    pq.push(5);
    pq.push(9);
    pq.push(2);
    pq.push(6);
    pq.push(5);
    pq.push(3);
    while (!pq.empty())
    {
        cout << pq.pop() << " ";
    }
}

实际运行输出:

(base) miglanigursimar@Miglanis-MacBook-Pro priorityqueue % ./a.out 
5 5 6 4 9 3 3 2 1 1 %

预期输出应为升序的π前10位数字:1 1 2 3 3 4 5 5 6 9


核心问题:pop函数的交换逻辑错误

在pop方法中,你交换的是局部变量elem和last,而非向量中的实际元素,导致堆的结构完全没有被正确修改。局部变量的交换不会影响vec容器内的元素,相当于每次只是取出堆顶元素,然后直接删除最后一个元素,完全破坏了堆的结构。

修复后的pop函数:

t pop()
{
    if (empty()) {
        throw std::runtime_error("Cannot pop from empty priority queue");
    }
    t elem = vec[0];
    // 直接交换堆顶与容器最后一个元素
    swap(&vec[0], &vec[vec.size() - 1]);
    vec.pop_back();
    sink(0);
    return elem;
}

可选优化:简化sink函数逻辑

原sink函数的嵌套条件判断过于复杂,容易出错。可以简化为先找到当前节点的最小子节点,若当前节点大于该子节点则交换并递归下沉,逻辑更清晰:

inline void sink(size_t i)
{
    size_t smallest = i;
    size_t left_ = left(i);
    size_t right_ = right(i);

    // 找到当前节点及其子节点中的最小值索引
    if (validindex(left_) && vec[left_] < vec[smallest]) {
        smallest = left_;
    }
    if (validindex(right_) && vec[right_] < vec[smallest]) {
        smallest = right_;
    }

    // 如果当前节点不是最小值,交换后继续下沉
    if (smallest != i) {
        swap(&vec[i], &vec[smallest]);
        sink(smallest);
    }
}

修复后运行程序,将得到预期的升序输出:1 1 2 3 3 4 5 5 6 9

内容的提问来源于stack exchange,提问作者Xowaga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 03:33:07