栈和队列

1、栈和队列

栈和队列都是以deque为底层容器的适配器。栈(stack)、队列(queue)、优先队列(priority_queue)这三种数据结构,准确的来说其实是一种适配器,因为底层都是以其他容器为基准。

栈:先入后出,只允许在栈顶添加和删除元素,称为入栈和出栈。

队列:先入先出,载队首取元素,在队尾添加元素,称为出队和入队。

优先队列:带权值的队列。

常见栈的应用场景包括括号问题的求解,表达式的转换和求值,函数调用和递归实现,深度优先遍历DFS等。

从源码中,可以证实stack和queue底层确实是使用deque作为底层容器封装。

stack源码:

#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class T, class Sequence = deque<T> >
#else
template <class T, class Sequence>
#endif
class stack {
public:
  typedef typename Sequence::value_type value_type;
  typedef typename Sequence::size_type size_type;
  typedef typename Sequence::reference reference;
  typedef typename Sequence::const_reference const_reference;
protected:
  Sequence c;

queque源码

 

#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class T, class Sequence = deque<T> >
#else
template <class T, class Sequence>
#endif
class queue {
public:
  typedef typename Sequence::value_type value_type;
  typedef typename Sequence::size_type size_type;
  typedef typename Sequence::reference reference;
  typedef typename Sequence::const_reference const_reference;
protected:
  Sequence c;

2、heap 堆

heap并不是一个容器,因此它没有实现自己的迭代器,也就没有遍历操作,所以heap只是一种算法。

push_back 插入元素

插入函数:push_heap,heap只接受RandomAccessIterator类型的迭代器。

template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {
  __push_heap_aux(first, last, distance_type(first), value_type(first));
}

template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*) {
    // 这里传入的是两个迭代器的长度, 0, 还有最后一个数据
  __push_heap(first, Distance((last - first) - 1), Distance(0),  T(*(last - 1)));
}

pop_heap 删除元素

pop操作其实并没有真正意义取删除数据,而是将数据放在最后,只是没有指向最后的元素而已,这里使用arrary也可以,毕竟美誉队数组的大小进行调整。

pop的实现有两种。

template <class RandomAccessIterator, class Compare>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                     Compare comp) {
    __pop_heap_aux(first, last, value_type(first), comp);
}
template <class RandomAccessIterator, class T, class Compare>
inline void __pop_heap_aux(RandomAccessIterator first,
                           RandomAccessIterator last, T*, Compare comp) {
  __pop_heap(first, last - 1, last - 1, T(*(last - 1)), comp,
             distance_type(first));
}
template <class RandomAccessIterator, class T, class Compare, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                       RandomAccessIterator result, T value, Compare comp,
                       Distance*) {
  *result = *first;
  __adjust_heap(first, Distance(0), Distance(last - first), value, comp);
}
template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                       RandomAccessIterator result, T value, Distance*) {
  *result = *first; // 因为这里是大根堆, 所以first的值就是最大值, 先将最大值保存.
  __adjust_heap(first, Distance(0), Distance(last - first), value);
}

make_heap 将数组编程堆存放

template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last) {
  __make_heap(first, last, value_type(first), distance_type(first));
}
template <class RandomAccessIterator, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
                 Distance*) {
  if (last - first < 2) return;
    // 计算长度, 并找出中间的根值
  Distance len = last - first;
  Distance parent = (len - 2)/2;
    
  while (true) {
      // 一个个进行调整, 放到后面
    __adjust_heap(first, parent, len, T(*(first + parent)));
    if (parent == 0) return;
    parent--;
  }
}

sort_heap实现堆排序

实际操作是每次将第一位数据弹出从而实现排序功能。

template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
  while (last - first > 1) pop_heap(first, last--);
}
template <class RandomAccessIterator, class Compare>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last,
               Compare comp) {
  while (last - first > 1) pop_heap(first, last--, comp);
}

3、优先队列priority_queue

priority_queue是一种带权值的优先队列,支持插入和删除操作,也只能从尾部插入、头部删除,并且其顺序也并非是根据加入的顺序排列。因为优先队列也是一种队列,所以它也就跟队列一样不能直接遍历数组,也没有迭代器。

priority_queque本身也不算是一种容器,它是以vector为容器以heap为数据操作的配置器。

类型定义:

#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class T, class Sequence = vector<T>, 
          class Compare = less<typename Sequence::value_type> >
#else
template <class T, class Sequence, class Compare>
#endif
class  priority_queue {
public:
 // 符合traits编程规范
  typedef typename Sequence::value_type value_type;
  typedef typename Sequence::size_type size_type;
  typedef typename Sequence::reference reference;
  typedef typename Sequence::const_reference const_reference;
protected:
  Sequence c; // 定义vector容器的对象
  Compare comp; // 定义比较函数(伪函数)
  ...
};

属性获取

priority_queue只有简单的3个熟悉获取函数,其本身的操作也简单,指向实现依赖了vector和heap就变得比较复杂。

class  priority_queue {
 ...
public:
  bool empty() const { return c.empty(); }
  size_type size() const { return c.size(); }
  const_reference top() const { return c.front(); }
    ...
};

push和pop实现

push和pop都是采用heap算法。

priority_queue本身是很复杂的,但是当我们了解vector和heap之后再来分析,它就变得简单了:即将vector作为容器,heap作为算法来操作的配置器。同时,这也体现了STL的灵活性:通过各种容器与算法的结合就能实现另一种功能。

最后,在生产环境中,stl所有的容器用法都有一个原则,即:避免拷贝开销,不要直接吧大的数据对象直接往容器里塞,而是使用指针。

posted @ 2021-08-14 20:38  钟齐峰  阅读(172)  评论(0编辑  收藏  举报