优先级队列

一、基础认知

1.核心特征

  • 容器适配器:默认vector为封装的容器
  • 堆结构:默认实现大顶堆(最大结构位于堆顶)
  • 自动排序:插入、删除自动维护堆结构
  • 访问限制:只能访问堆顶元素

2.堆类型对比

类型声明方式仿函数堆顶元素特征
大顶堆priority_queue<int>less<T>最大值
小顶堆priority_queue<int,vector<int>,greater<int>>greater<T>最小值

二、核心实现机制

1.仿函数(核心控制逻辑)

(1)本质与作用

  • 本质:像函数一样使用的类对象
  • 函数对象:重载了operator()的类实例
  • 控制策略:通过比较结果决定元素位置
  • 模板参数:Compare控制堆类型(默认less<T>)

(2)标准实现

// 大顶堆比较器(默认)
template<class T>
class Less {
public:
    bool operator()(const T& x, const T& y) {
        return x < y;  // 父节点 < 子节点时交换
    }
};

// 小顶堆比较器
template<class T>
class Greater {
public:
    bool operator()(const T& x, const T& y) {
        return x > y;  // 父节点 > 子节点时交换
    }
};

(3)指针类型的特殊处理

// 错误情况:直接使用默认比较器
priority_queue<int*> pq;  // 比较指针地址而非实际值

// 正确方案:自定义仿函数
struct PtrCompare {
    bool operator()(const int* a, const int* b) {
        return *a < *b;  // 解引用比较实际值
    }
};
priority_queue<int*, vector<int*>, PtrCompare> pq;

三、底层实现剖析

1.类模板结构

namespace lynn {
    template<class T,class Container = vector<T>,class Compare = less<T>>
    class priority_queue {
    private:
        Container _con;     // 底层容器(默认vector)
        Compare _comp;      // 比较器对象
        // 堆调整算法...
    };
}

2.关键算法实现

(1)向下调整算法(堆化)

void AdjustDown(int parent) {
    size_t child = parent * 2 + 1;  // 初始左孩子
    while (child < _con.size()) {
        // 选择符合条件的子节点
        if (child+1 < _con.size() && _comp(_con[child], _con[child+1]))
            ++child;
        
        // 父子节点比较交换
        if (_comp(_con[parent], _con[child])) {
            swap(_con[parent], _con[child]);
            parent = child;
            child = parent * 2 + 1;
        } else {
            break;
        }
    }
}

(2)向上调整算法(插入维护)

void AdjustUp(int child) {
    int parent = (child-1)/2;
    while (child > 0) {
        if (_comp(_con[parent], _con[child])) {
            swap(_con[child], _con[parent]);
            child = parent;
            parent = (child-1)/2;
        } else {
            break;
        }
    }
}

3.堆构造

template<class InputIterator>
priority_queue(InputIterator first, InputIterator last) {
    // 数据填充
    while (first != last) {
        _con.push_back(*first++);
    }
    
    // Floyd建堆算法(从最后一个非叶子节点开始)
    for (int i = (_con.size()-2)/2; i >= 0; --i) {
        AdjustDown(i);  // O(N)时间复杂度
    }
}

四、核心接口实现

1.元素操作

操作实现原理时间复杂度
push()尾部插入后向上调整O(log N)
pop()首尾交换后删除,堆顶向下调整O(log N)
top()直接返回首元素O(1)

 2.代码实现

void push(const T& x) {
    _con.push_back(x);
    AdjustUp(_con.size()-1);  // 新元素上浮
}

void pop() {
    swap(_con[0], _con.back());
    _con.pop_back();
    AdjustDown(0);  // 新堆顶下沉
}

const T& top() const {
    return _con[0];
}

五、关键问题解析

1.仿函数的工作时机

触发时机比较方向
AdjustUp向上调整时子节点与父节点
AdjustDown向下调整时父节点与子节点
循环调用AdjustDown时多层级比较

2.推荐容器

  • vector:默认选择,连续内存访问
  • deque:分段连续内存结构

六、易错点提醒

1.仿函数方向混淆

  • 大顶堆使用less<T>:当父节点<子节点时交换。
  • 小顶堆使用greater<T>:当父节点>子节点时交换。

2.指针类型陷阱

// 错误示例:直接比较指针地址
   priority_queue<string*> pq;  
   pq.push(new string("apple"));
   pq.push(new string("banana"));  // 比较的是内存地址!

   // 正确方案:自定义解引用比较器
   struct StringPtrCompare {
       bool operator()(const string* a, const string* b) {
           return *a < *b;  // 按字符串内容排序
       }
   };

3.无效堆状态

  • 禁止直接修改容器元素
  • 修改后需要重新调用建堆
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值