STL容器适配器之priority_queue

本文详细介绍了C++STL中的优先级队列及其使用,包括模板结构、堆的原理、排序方法以及仿函数的应用。同时探讨了反向迭代器的实现和适配器模式在其中的作用。
摘要由CSDN通过智能技术生成

补充:

​ 适配器不仅是对容器进行了包装,而且有的会进行深加工,如优先级队列,对数据进行了调整。

一、优先级队列

​ 声明存放在stl的queue头文件中。

template <class T, class Container = vector<T>,
  class Compare = less<typename Container::value_type> > class priority_queue;

​ 1.第二个模板参数的缺省参数是vector<T>,因为排序对于vector更快;

​ 2.第三个模板参数是仿函数,用来控制优先级,然后输出;

​ 3.优先级队列的底层是二叉树的堆,默认是大堆,可以使用仿函数实现小堆;

​ 4.升序建大堆,降序建小堆,如降序,先建前k个为小堆,然后遍历剩下的元素往小堆里替换,最后一个元素就是第k个最大值;

​ 5.有n个元素向上调整的时间复杂度就是O(lgn),向下调整就是O(lgn),向下调整建堆是O(n),但是有个前提就是左右子树是大堆或者小堆;

二、优先级队列的简单使用

1.常用接口

在这里插入图片描述

2.构造函数

​ 与栈和队列不同的是除了默认构造外,还有一个迭代器区间构造。

template <class InputIterator>
priority_queue (InputIterator first, InputIterator last,
                const Compare& comp = Compare(),
                const Container& ctnr = Container());

3.简单使用

void test_priority_queue()
{
    priority_queue<int, vector<int>, greater<int>> pq;
    pq.push(1);
    pq.push(2);
    pq.push(3);
    pq.push(4);
    pq.push(5);
    while (!pq.empty())
    {
        cout << pq.top() << " ";
        pq.pop();
    }
    cout << endl;
}
int main()
{
    test_priority_queue();
    return 0;
}

三、模拟实现优先级队列

namespace Priority_queue
{
    template <class T, class Container = std::vector<T>>
    class priority_queue
    {
        void adjustDown(int parent) // 最后一个叶节点的父节点
        {
            // 找左右孩子较大的
            int child = parent * 2 + 1; // 默认左孩子大,使用仿函数则将比较顺序改为左孩子 比较 右孩子
            while (child < con_.size())
            {
                if (child + 1 < con_.size() && con_[child + 1] > con_[child])
                {
                    child++;
                }
                if (con_[child] > con_[parent])
                {
                    std::swap(con_[child], con_[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }
        void adjustUp(int child)
        {
            int parent = (child - 1) / 2;
            while (parent >= 0)
            {
                if (con_[child] > con_[parent])
                {
                    std::swap(con_[child], con_[parent]);
                    child = parent;
                    parent = (parent - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

    public:
        // 默认成员函数
        // 无参构造
        priority_queue() {}
        // 带区间的构造函数
        template <class InputIterator>
        priority_queue(InputIterator first, InputIterator last)
        {
            while (first != last)
            {
                con_.push_back(*first);
                ++first;
            }
            // 向下调整建堆
            for (int i = (con_.size() - 1 - 1) / 2; i >= 0; i--) // 下标
            {
                adjustDown(i);
            }
        }
        // 普通成员函数
        void pop()
        {
            std::swap(con_[0], con_[con_.size() - 1]);
            con_.pop_back();
            adjustDown(0);
        }
        void push(const T &val)
        {
            // 先尾部插入一个数据,在进行向上调整
            con_.push_back(val);
            adjustUp(con_.size() - 1);
        }
        const T &top()
        {
            return con_[0];
        }
        size_t size()
        {
            return con_.size();
        }
        bool empty()
        {
            return con_.empty();
        }

    private:
        Container con_;
    };
}

四、仿函数

​ 1.由于c语言的函数指针使用起来十分繁琐,所以想用仿函数来代替函数指针使用;

​ 2.想要将函数内部哪部分的比较方式,通过外界进行修改,就将哪部分用仿函数实现;

1.什么是仿函数

​ 1.仿函数又叫做函数对象,其中仿函数更多的指的是类,函数对象指的是实例化出来的对象;

​ 2.仿函数实际上就是用一个类封装了一些运算符重载,使得函数对象可以像函数一样去使用;

​ 3.以前的泛型编程是类和函数针对数据的泛型,而仿函数实现了,对比较方式的泛型编程;

2.仿函数的使用

template<class T>
class less
{
    public:
    bool operator(){}
};

3.仿函数特性

​ 1.仿函数没有成员对象,仅仅是为了使用函数;

4.必须实现仿函数的场景

​ 1.比如传入的参数是指针,但是想要比较的是指针指向的内容,如日期类指针比较大小想比较指针的内容,可是已经重载过比较了。指针类型是内置类型,而内置类型的运算符在类外是不可以改变原来效果,所以需要用仿函数来修改,operator(),可以传递多个参数,而且不会和类内定义的冲突。

五、反向迭代器

​ 库里面使用了适配器模式来实现反向迭代器,而不是在正向迭代器的基础上对++和–做修改,做很多的重复工作。

​ 反向迭代器定义在了iterator.h里面,是一个用正向迭代器适配的类模板,适用于所有容器;

template <class Iterator>
class reverse_iterator 
{
protected:
    Iterator current;
    public:
    typedef typename iterator_traits<Iterator>::iterator_category
        iterator_category;
    typedef typename iterator_traits<Iterator>::value_type
        value_type;
    typedef typename iterator_traits<Iterator>::difference_type
        difference_type;
    typedef typename iterator_traits<Iterator>::pointer
        pointer;
    typedef typename iterator_traits<Iterator>::reference
        reference;

    typedef Iterator iterator_type;
    typedef reverse_iterator<Iterator> self;

public:
    reference operator*() const {
        Iterator tmp = current;
        return *--tmp;
    }
    #ifndef __SGI_STL_NO_ARROW_OPERATOR
    pointer operator->() const { return &(operator*()); }
    #endif /* __SGI_STL_NO_ARROW_OPERATOR */

    self& operator++() {
        --current;
        return *this;
    }
    self operator++(int) {
        self tmp = *this;
        --current;
        return tmp;
    }
    self& operator--() {
        ++current;
        return *this;
    }
    self operator--(int) {
        self tmp = *this;
        ++current;
        return tmp;
    }

    self operator+(difference_type n) const {
        return self(current - n);
    }
    self& operator+=(difference_type n) {
        current -= n;
        return *this;
    }
    self operator-(difference_type n) const {
        return self(current + n);
    }
    self& operator-=(difference_type n) {
        current += n;
        return *this;
    }
    reference operator[](difference_type n) const { return *(*this + n); }  
}; 

template <class Iterator>
inline bool operator==(const reverse_iterator<Iterator>& x, 
                       const reverse_iterator<Iterator>& y) {
    return x.base() == y.base();
}

template <class Iterator>
inline bool operator<(const reverse_iterator<Iterator>& x, 
                      const reverse_iterator<Iterator>& y) {
    return y.base() < x.base();
}

template <class Iterator>
inline typename reverse_iterator<Iterator>::difference_type
operator-(const reverse_iterator<Iterator>& x, 
          const reverse_iterator<Iterator>& y) {
    return y.base() - x.base();
}

template <class Iterator>
inline reverse_iterator<Iterator> 
operator+(reverse_iterator<Iterator>::difference_type n,
          const reverse_iterator<Iterator>& x) {
    return reverse_iterator<Iterator>(x.base() - n);
}

1.总结

​ 1.反向迭代器+就转换成了正向迭代器的-;

​ 2.对于”*对象和对象指针->“不是调用正向迭代器的“*对象和对象指针->”,而是访问前一个位置,原因是rbegin()调用的是end(),rend()调用的是begin(),这样就实现了迭代器镜像对称。

2.模拟实现反向迭代器

​ 1.成员函数的返回值类型实现的方式有两种,1.迭代器萃取;2.传递模板参数(更容易一些);

​ 2.内置类型使用浅拷贝满足条件

namespace Reverse_iterator
{
    template <class Iterator, class Ref, class Ptr>
    class reverse_iterator
    {
    public:
        typedef reverse_iterator<Iterator, Ref, Ptr> Self;

    public:
        // 默认成员函数
        reverse_iterator(Iterator it) : current_(it) {}

    public:
        // 普通成员函数
        Ref operator*()
        {
            Iterator tmp(current_);
            return *(--tmp);
        }
        Ptr operator->()
        {
            return &operator*();
        }
        Self &operator++()
        {
            --current_;
            return *this;
        }
        Self &operator--()
        {
            ++current_;
            return *this;
        }
        Self operator++(int)
        {
            Self tmp(current_);
            current_--;
            return tmp;
        }
        Self operator--(int)
        {
            Self tmp(current_);
            current_++;
            return tmp;
        }
        bool operator!=(const Self &iterator) const
        {
            return current_ != iterator.current_;
        }
        bool operator==(const Self &iterator) const
        {
            return current_ == iterator.current_;
        }

    private:
        Iterator current_;
    };
}
  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值