目錄
一、stack的介紹和使用
1.1 stack的介紹
1.2 stack的使用
1.3 stack的模擬實現
二、queue的介紹和使用
2.1 queue的介紹
2.2 queue的使用
2.3 queue的模擬實現
三、priority_queue的介紹和使用
3.1 priority_queue的介紹和使用
3.2 priority_queue的使用
3.4 priority_queue的模擬實現
四、容器適配器
4.1 什么是適配器
4.2 STL標準庫中stack和queue的底層結構
4.3 deque的簡單介紹
4.3.1 deque的原理介紹
4.3.2 deque的缺陷
4.4 為什么選擇deque作為stack和queue的底層默認容器
4.5 STL標準庫中對于stack和queue的模擬實現
4.5.1?stack的模擬實現
4.5.2 queue的模擬實現
一、stack的介紹和使用
1.1 stack的介紹
stack的文檔介紹
- stack是一種容器適配器,專門用在具有后進先出操作的上下文環境中,其刪除只能從容器的一端進行元素的插入與提取操作。
- stack是作為容器適配器被實現的,容器適配器即是對特定類封裝作為其底層的容器,并提供一組特定的成員函數來訪問其元素,將特定類作為其底層的,元素特定容器的尾部(即棧頂)被壓入和彈出。
- stack的底層容器可以是任何標準的容器類模板或者一些其他特定的容器類,這些容器類應該支持以下操作:
- empty:判空操作
- back:獲取尾部元素操作
- push_back:尾部插入元素操作
- pop_back:尾部刪除元素操作
- 標準容器vector、deque、list均符合這些需求,默認情況下,如果沒有為stack指定特定的底層容器,默認情況下使用deque。
1.2 stack的使用
函數說明 | 接口說明 |
stack() | 構造空的棧 |
empty() | 檢測stack是否為空 |
size() | 返回stack中元素的個數 |
top() | 返回棧頂元素的引用 |
push() | 將元素val壓入stack中 |
pop() | 將stack中尾部的元素彈出 |
例題:
LeetCode155 最小棧
class MinStack
{
public: void push(int x){ // 只要是壓棧,先將元素保存到_elem中_elem.push(x);// 如果x小于_min中棧頂的元素,將x再壓入_min中if(_min.empty() || x <= _min.top())_min.push(x);}void pop(){// 如果_min棧頂的元素等于出棧的元素,_min頂的元素要移除if(_min.top() == _elem.top())_min.pop();_elem.pop();}int top(){return _elem.top();}int getMin(){return _min.top();}private:// 保存棧中的元素std::stack<int> _elem;// 保存棧的最小值std::stack<int> _min;
};
牛客網JZ31 棧的壓入、彈出序列
class Solution
{
public:bool IsPopOrder(vector<int> pushV,vector<int> popV){//入棧和出棧的元素個數必須相同if(pushV.size() != popV.size())return false;// 用s來模擬入棧與出棧的過程int outIdx = 0;int inIdx = 0;stack<int> s;while(outIdx < popV.size()){// 如果s是空,或者棧頂元素與出棧的元素不相等,就入棧while(s.empty() || s.top() != popV[outIdx]){if(inIdx < pushV.size())s.push(pushV[inIdx++]);elsereturn false;}// 棧頂元素與出棧的元素相等,出棧s.pop();outIdx++;}return true;}
};
LeetCode150 逆波蘭表達式求值
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> s;for (size_t i = 0; i < tokens.size(); ++i){string& str = tokens[i];// str為數字if (!("+" == str || "-" == str || "*" == str || "/" == str)){s.push(atoi(str.c_str()));}else{// str為操作符int right = s.top();s.pop();int left = s.top();s.pop();switch (str[0]){case '+':s.push(left + right);break;case '-':s.push(left - right);break;case '*':s.push(left * right);break;case '/':// 題目說明了不存在除數為0的情況s.push(left / right);break;}}}return s.top();}
};
1.3 stack的模擬實現
????????從棧的接口中可以看出,棧實際是一種特殊的vector,因此使用vector完全可以模擬實現stack。
#include<vector>
namespace bite
{template<class T>class stack{public:stack() {}void push(const T& x) {_c.push_back(x);}void pop() {_c.pop_back();}T& top() {return _c.back();}const T& top()const {return _c.back();}size_t size()const {return _c.size();}bool empty()const {return _c.empty();}private:std::vector<T> _c;};
}
二、queue的介紹和使用
2.1 queue的介紹
queue的文檔介紹
- 隊列是一種容器適配器,專門用于在FIFO上下文(先進先出)中操作,其中從容器一端插入元素,另一端提取元素。
- 隊列作為容器適配器實現,容器適配器即將特定容器類封裝作為其底層容器類,queue提供一組特定的成員函數來訪問其元素。元素從隊尾入隊列,從隊頭出隊列。
- 底層容器可以是標準容器類模板之一,也可以是其他專門設計的容器類。該底層容器應至少支持以下操作:
- empty:檢測隊列是否為空
- size:返回隊列中有效元素的個數
- front:返回隊頭元素的引用
- back:返回隊尾元素的引用
- push_back:在隊列尾部入隊列
- pop_front:在隊列頭部出隊列
- 標準容器類deque和list滿足了這些要求。默認情況下,如果沒有為queue實例化指定容器類,則使用標準容器deque。
2.2 queue的使用
函數聲明 | 接口說明 |
queue() | 構造空的隊列 |
empty() | 檢測隊列是否為空,是返回true,否則返回false |
size() | 返回隊列中有效元素的個數 |
front() | 返回隊頭元素的引用 |
back() | 返回隊尾元素的引用 |
push() | 在隊尾將元素val入隊列 |
pop() | 將隊頭元素出隊列 |
2.3 queue的模擬實現
#include <list>
namespace bite
{template<class T>class queue{public:queue() {}void push(const T& x) {_c.push_back(x);}void pop() {_c.pop_front();}T& back() {return _c.back();}const T& back()const {return _c.back();}T& front() {return _c.front();}const T& front()const {return _c.front();}size_t size()const {return _c.size();}bool empty()const {return _c.empty();}private:std::list<T> _c;};
}
三、priority_queue的介紹和使用
3.1 priority_queue的介紹和使用
priority_queue文檔介紹
- 優先隊列是一種容器適配器,根據嚴格的弱排序標準,它的第一個元素總是它所包含的元素中最大的。
- 此上下文類似于堆,在堆中可以隨時插入元素,并且只能檢索最大堆元素(優先隊列中位于頂部的元素)。
- 優先隊列被實現為容器適配器,容器適配器即將特定容器類封裝作為其底層容器類,queue提供一組特定的成員函數來訪問其元素。元素從特定容器的“尾部”彈出,其稱為優先隊列的頂部。
- 底層容器可以是任何標準容器類模板,也可以是其他特定設計的容器類。容器應該可以通過隨機訪問迭代器訪問,并支持以下操作:
- empty():檢測容器是否為空
- size():返回容器中有效元素個數
- front():返回容器中第一個元素的引用
- push_back():在容器尾部插入元素
- pop_back():刪除容器尾部元素
- 標準容器類vector和deque滿足這些需求。默認情況下,如果沒有為特定的priority_queue類實例化指定容器類,則使用vector。
- 需要支持隨機訪問迭代器,以便始終在內部保持堆結構。容器適配器通過在需要時自動調用算法函數make_heap、push_heap和pop_heap來自動完成此操作。
3.2 priority_queue的使用
函數聲明 | 接口說明 |
priority_queue()/priority_queue(first, last) | 構造一個空的優先級隊列 |
empty() | 檢測優先級隊列是否為空,是返回 true ,否則返回 false |
top() | 返回優先級隊列中最大(最小元素),即堆頂元素 |
push() | 在優先級隊列中插入元素x |
pop() | 刪除優先級隊列中最大(最小)元素,即堆頂元素 |
#include <vector>
#include <queue>
#include <functional> // greater算法的頭文件
void TestPriorityQueue()
{// 默認情況下,創建的是大堆,其底層按照小于號比較vector<int> v{3,2,7,6,0,4,1,9,8,5};priority_queue<int> q1;for (auto& e : v)q1.push(e);cout << q1.top() << endl;// 如果要創建小堆,將第三個模板參數換成greater比較方式priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;
}
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
private:int _year;int _month;int _day;
};void TestPriorityQueue()
{// 大堆,需要用戶在自定義類型中提供<的重載priority_queue<Date> q1;q1.push(Date(2018, 10, 29));q1.push(Date(2018, 10, 28));q1.push(Date(2018, 10, 30));cout << q1.top() << endl;// 如果要創建小堆,需要用戶提供>的重載priority_queue<Date, vector<Date>, greater<Date>> q2;q2.push(Date(2018, 10, 29));q2.push(Date(2018, 10, 28));q2.push(Date(2018, 10, 30));cout << q2.top() << endl;
}
例題:LeetCode215 數組中的第K個最大元素
class Solution
{
public:int findKthLargest(vector<int>& nums, int k) {// 將數組中的元素先放入優先級隊列中priority_queue<int> p(nums.begin(), nums.end());// 將優先級隊列中前k-1個元素刪除掉for(int i= 0; i < k-1; ++i){p.pop();}return p.top();}
};
3.4 priority_queue的模擬實現
#pragma once#include <iostream>
using namespace std;#include <vector>
// priority_queue--->堆
namespace casso
{template<class T>struct less{bool operator()(const T& left, const T& right){return left < right;}};template<class T>struct greater{bool operator()(const T& left, const T& right){return left > right;}};template<class T, class Container = std::vector<T>, class Compare = less<T>>class priority_queue{public:// 創造空的優先級隊列priority_queue() : c() {}template<class Iterator>priority_queue(Iterator first, Iterator last): c(first, last){// 將c中的元素調整成堆的結構int count = c.size();int root = ((count - 2) >> 1);for (; root >= 0; root--)AdjustDown(root);}void push(const T& data){c.push_back(data);AdjustUP(c.size() - 1);}void pop(){if (empty())return;swap(c.front(), c.back());c.pop_back();AdjustDown(0);}size_t size()const{return c.size();}bool empty()const{return c.empty();}// 堆頂元素不允許修改,因為:堆頂元素修改可以會破壞堆的特性const T& top()const{return c.front();}private:// 向上調整void AdjustUP(int child){int parent = ((child - 1) >> 1);while (child){if (Compare()(c[parent], c[child])){swap(c[child], c[parent]);child = parent;parent = ((child - 1) >> 1);}else{return;}}}// 向下調整void AdjustDown(int parent){size_t child = parent * 2 + 1;while (child < c.size()){// 找以parent為根的較大的孩子if (child + 1 < c.size() && Compare()(c[child], c[child + 1]))child += 1;// 檢測雙親是否滿足情況if (Compare()(c[parent], c[child])){swap(c[child], c[parent]);parent = child;child = parent * 2 + 1;}elsereturn;}}private:Container c;};
}void TestQueuePriority()
{casso::priority_queue<int> q1;q1.push(5);q1.push(1);q1.push(4);q1.push(2);q1.push(3);q1.push(6);cout << q1.top() << endl;q1.pop();q1.pop();cout << q1.top() << endl;vector<int> v{ 5,1,4,2,3,6 };casso::priority_queue<int, vector<int>, casso::greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;q2.pop();q2.pop();cout << q2.top() << endl;
}
四、容器適配器
4.1 什么是適配器
4.2 STL標準庫中stack和queue的底層結構
4.3 deque的簡單介紹
4.3.1 deque的原理介紹
4.3.2 deque的缺陷
- 與vector比較,deque的優勢是:頭部插入和刪除時,不需要搬移元素,效率特別高,而且在擴容時,也不需要搬移大量的元素,因此其效率是必vector高的。
- 與list比較,其底層是連續空間,空間利用率比較高,不需要存儲額外字段。
- 但是,deque有一個致命缺陷:不適合遍歷,因為在遍歷時,deque的迭代器要頻繁的去檢測其是否移動到某段小空間的邊界,導致效率低下,而序列式場景中,可能需要經常遍歷,因此在實際中,需要線性結構時,大多數情況下優先考慮vector和list,deque的應用并不多,而目前能看到的一個應用就是,STL用其作為stack和queue的底層數據結構
4.4 為什么選擇deque作為stack和queue的底層默認容器
- stack和queue不需要遍歷(因此stack和queue沒有迭代器),只需要在固定的一端或者兩端進行操作。
- 在stack中元素增長時,deque比vector的效率高(擴容時不需要搬移大量數據);queue中的元素增長時,deque不僅效率高,而且內存使用率高。
4.5 STL標準庫中對于stack和queue的模擬實現
4.5.1?stack的模擬實現
#include <deque>
namespace casso
{template<class T, class Con = deque<T>>//template<class T, class Con = vector<T>>//template<class T, class Con = list<T>>class stack{public:stack() {}void push(const T& x) {_c.push_back(x);}void pop() {_c.pop_back();}T& top() {return _c.back();}const T& top()const {return _c.back();}size_t size()const {return _c.size();}bool empty()const {return _c.empty();}private:Con _c;};
}
4.5.2 queue的模擬實現
#include <deque>
#include <list>
namespace casso
{template<class T, class Con = deque<T>>//template<class T, class Con = list<T>>class queue{public:queue() {}void push(const T& x) {_c.push_back(x);}void pop() {_c.pop_front();}T& back() {return _c.back();}const T& back()const {return _c.back();}T& front() {return _c.front();}const T& front()const {return _c.front();}size_t size()const {return _c.size();}bool empty()const {return _c.empty();}private:Con _c;};
}