1. 容器适配器
stack 和 queue 有一点需要注意,虽然 stack 和 queue 也可以存放元素,但在 STL 中并没有将其划分在容器的行列,而是将其称为容器适配器。STL 中stack 和 queue默认使用deque。在 stack 和 queue 的类模版声明中我们可以看到,它们的模版参数有两个,第一个是 stack 和 queue 当中所存储的元素类型,而另一个则是指定使用的容器类型,只不过当我们不指定使用何种容器的情况下,stack 和 queue 默认使用 deque 作为容器。
2. stack 的模拟实现
2.1. stack.h
#include <iostream>
using namespace std;
#include <deque>
namespace MyStack {
template<class T, class Container = deque<T> >
class stack {
public:
void push(const T& x);
void pop();
T& top();
const T& top()const;
size_t size()const;
bool empty();
private:
Container _Container;
};
};
2.1.1. push 和 pop
栈的特点是后进先出,那么对于 push 我们可以调用容器适配器的 push_back,由于是后进先出,那么 pop 调用的则是 pop_back。
void push(const T& x) {
_Container.push_back(x);
}
void pop() {
_Container.pop_back();
}
2.1.2. top 和 size 以及 empty
栈的 top 指的是栈顶元素,那么对应的则应该是容器适配器的 back() 函数,由于会存在 const 对象调用 top 函数,那么因此我们要对其进行函数重载。size 和 empty 直接调用容器适配器的 size() 和 empty 函数即可。
const T& top()const { return _Container.back(); }
size_t size()const { return _Container.size(); }
bool empty() { return _Container.empty(); }
2.2. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"
#include <vector>
int main() {
MyStack::stack<int,vector<int>> st1;
st1.push(1);
st1.push(2);
st1.push(3);
st1.push(4);
st1.push(5);
cout << "size:> " << st1.size() << endl;
while (!st1.empty()) {
cout << st1.top() << " ";
st1.pop();
}
return 0;
}
3. stack 的总代码
3.1. stack.h
#include <iostream>
using namespace std;
#include <deque>
namespace MyStack {
template<class T, class Container = deque<T> >
class stack {
public:
void push(const T& x) {
_Container.push_back(x);
}
void pop() {
_Container.pop_back();
}
T& top() { return _Container.back(); }
const T& top()const { return _Container.back(); }
size_t size()const { return _Container.size(); }
bool empty() { return _Container.empty(); }
private:
Container _Container;
};
};
3.2. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"
#include <vector>
int main() {
MyStack::stack<int,vector<int>> st1;
st1.push(1);
st1.push(2);
st1.push(3);
st1.push(4);
st1.push(5);
cout << "size:> " << st1.size() << endl;
while (!st1.empty()) {
cout << st1.top() << " ";
st1.pop();
}
return 0;
}
4. queue 的模拟实现
4.1. Queue.h
#include <iostream>
using namespace std;
#include <deque>
namespace MyQueue {
template<class T, class Container = deque<T>>
class queue {
public:
void push(const T& value);
void pop();
T& back();
T& back() const;
T& front();
T& front() const;
size_t size()const;
bool empty() const;
private:
Container _Container;
};
};
4.1.1. push 和 pop
队列的特点是先进先出,那么对于 push 我们可以调用容器适配器的 push_back,由于是先进先出,那么 pop 调用的则是 pop_front。
void push(const T& value) {
_Container.push_back(value);
}
void pop() {
_Container.pop_front();
}
4.1.2. back 和 front 和 size 与 empty
队列的 back 指的是对头元素,那么对应的则应该是容器适配器的 back() 函数,由于会存在 const 对象调用 back 函数,那么因此我们要对其进行函数重载。队列的 front 指的是队尾元素,那么对应的则应该是容器适配器的 front() 函数,同理 back 函数我们也要对其进行函数重载。size 和 empty 直接调用容器适配器的 size() 和 empty 函数即可。
4.2. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"
#include <list>
int main() {
MyQueue::queue<int, list<int>> q1;
q1.push(1);
q1.push(2);
q1.push(3);
q1.push(4);
q1.push(5);
while (!q1.empty()) {
cout << q1.front() << "::" << q1.back() << endl;;
q1.pop();
}
return 0;
}
5. queue 的总代码
5.1. Queue.h
#include <iostream>
using namespace std;
#include <deque>
namespace MyQueue {
template<class T, class Container = deque<T>>
class queue {
public:
void push(const T& value) {
_Container.push_back(value);
}
void pop() {
_Container.pop_front();
}
T& back() { return _Container.back(); }
T& back() const { return _Container.back(); }
T& front() { return _Container.front(); }
T& front() const { return _Container.front(); }
size_t size() { _Container.(); }
{ _Container.(); }
:
Container _Container;
};
};
5.2. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"
#include <list>
int main() {
MyQueue::queue<int, list<int>> q1;
q1.push(1);
q1.push(2);
q1.push(3);
q1.push(4);
q1.push(5);
while (!q1.empty()) {
cout << q1.front() << "::" << q1.back() << endl;
q1.pop();
}
return 0;
}
6. priority_queue 的模拟实现
6.1. priority_queue.h
#pragma once
#include <iostream>
using namespace std;
#include <assert.h>
#include <vector>
#include <algorithm>
namespace MyPriorityQueue {
template <class T,class Container = vector<T>,class Compare = less<T>>
class priority_queue {
public:
void Adjust_Up(int child);
void Adjust_Down(int parent);
void push(const T& val);
void pop();
bool empty() const;
size_t size() const;
T& top();
private:
Container _Container;
};
}
6.1.1. 向上调整算法
- 将目标节点与其父节点进行比较。
- 若目标节点的值比父节点大,则进行交换,并且同时将原目标节点的父亲节点当作新的目标节点进行向上调整,若目标结点的值比其父结点的值小,则停止向上调整,此时该树已经是大堆了。
void Adjust_Up(int child) {
int parent = (child - 1) / 2;
while(child > 0) {
if(_Container[child] > _Container[parent]) {
swap(_Container[child],_Container[parent]);
child = parent;
parent = (child - 1) / 2;
} else {
break;
}
}
}
6.1.2. 向下调整算法
使用向下调整算法有一个前提:若想将其调整为小堆,那么根结点的左右子树必须都为小堆。若想将其调整为大堆,那么根结点的左右子树必须都为大堆。
- 从根节点开始,选出左右孩子中的最大值。
- 让最大的孩子与父亲进行比较。
- 若孩子比父亲大,则让该孩子的与其父亲的位置进行交换。并将原来大的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止
- 若若大的孩子比父亲小,则不需处理了,调整完成,整个树已经是大堆了。
void Adjust_Down(int parent) {
int child = parent * 2 + 1;
while (child < size()) {
if(child + 1 < size() && _Container[child + 1] > _Container[child]) {
child++;
}
if (_Container[child] > _Container[parent]) {
swap(_Container[child], _Container[parent]);
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}
6.1.3. push 和 pop
对于 push,直接尾插,然后进行向上调整算法即可。对于 pop,删除的是堆顶的元素,但是这个删除过程可并不是直接删除堆顶的数据,而是先将堆顶的数据与最后一个结点的位置交换,然后再删除最后一个结点,再对堆进行一次向下调整。若是直接删除堆顶的数据,那么原堆后面数据的父子关系就全部打乱了,需要全体重新建堆,时间复杂度为 O(N)。若是用上述方法,那么只需要对堆进行一次向下调整即可,因为此时根结点的左右子树都是小堆,我们只需要在根结点处进行一次向下调整即可,时间复杂度为 O(log(N))。
void push(const T& val) {
_Container.push_back(val);
Adjust_Up(size() - 1);
}
void pop() {
swap(_Container[0], _Container[size() - 1]);
_Container.pop_back();
Adjust_Down(0);
}
6.1.4. empty 与 size 与 top
bool empty() const { return _Container.empty(); }
size_t size() const { return _Container.size(); }
T& top() { return _Container[0]; }
6.2. Test.cpp
#include "Priority_queue.h"
int main() {
MyPriorityQueue::priority_queue<int> pq;
pq.push(70);
pq.push(56);
pq.push(30);
pq.push(15);
pq.push(25);
pq.push(10);
pq.push(90);
pq.push(100);
while(!pq.empty()) {
cout << pq.top() << " " << "size:><<" << pq.size() << endl;
pq.pop();
}
return 0;
}
7. priority_queue 的总代码
7.1. priority_queue.h
#pragma once
#include <iostream>
using namespace std;
#include <assert.h>
#include <vector>
#include <algorithm>
namespace MyPriorityQueue {
template <class T,class Container = vector<T>,class Compare = less<T>>
class priority_queue {
public:
void Adjust_Up(int child) {
int parent = (child - 1) / 2;
while(child > 0) {
if(_Container[child] > _Container[parent]) {
swap(_Container[child],_Container[parent]);
child = parent;
parent = (child - 1) / 2;
} else {
break;
}
}
}
void Adjust_Down(int parent) {
int child = parent * 2 + ;
(child < ()) {
(child + < () && _Container[child + ] > _Container[child]) {
child++;
}
(_Container[child] > _Container[parent]) {
(_Container[child], _Container[parent]);
parent = child;
child = parent * + ;
} {
;
}
}
}
{
_Container.(val);
(() - );
}
{
(_Container[], _Container[() - ]);
_Container.();
();
}
{ _Container.(); }
{ _Container.(); }
{ _Container[]; }
:
Container _Container;
};
}
7.2. Test.cpp
#include "Priority_queue.h"
int main() {
MyPriorityQueue::priority_queue<int> pq;
pq.push(70);
pq.push(56);
pq.push(30);
pq.push(15);
pq.push(25);
pq.push(10);
pq.push(90);
pq.push(100);
while(!pq.empty()) {
cout << pq.top() << " " << "size:><<" << pq.size() << endl;
pq.pop();
}
return 0;
}
8. 反向迭代器
反向迭代器与正向迭代器的区别主要在遍历的方向上:正向迭代器是从容器的第一个元素开始向后遍历的,反向迭代器是从容器的最后一个元素开始向前遍历的。因此我们可以通过对正向迭代器的封装来生成反向迭代器----迭代器适配器。
反向迭代器在实现的时候与正向迭代器是一种镜像对称的关系。
使用正向迭代器适配反向迭代器的时候会认为它两存在对称关系,那么此时就会导致两个问题 rbegin 第一次解引用是一个随机值。rbegin == rend 就结束了
那么就导致了第一个元素没有取到,那么我们该如何解决这个对称的坑呢?通过解引用来解决这个坑,解引用时,我们并不是解引用当前位置,而是解引用它的前一个位置!
反向迭代器的++是正向迭代器的--,反向迭代器的--就是正向迭代器的++。
8.1. 代码实现
#define _CRT_SECURE_NO_WARNINGS
namespace MyReverseIterator {
template<class Iterator,class Reference,class Pointer>
class ReverseIterator {
typedef ReverseIterator<Iterator, Reference, Pointer> Self;
ReverseIterator(Iterator it) :_it(it) { };
Self& operator++() { --_it; return *this; }
Self& operator--() { ++_it; return *this; }
Reference operator*() {
Iterator temp = _it;
--temp;
return *temp;
}
Pointer operator->() { return &(operator*()) }
bool operator!=(const Self& rit) { return _it != rit._it; }
bool operator==(const Self& rit) { return _it == rit._it; }
private:
Iterator _it;
};
}
8.2. 反向迭代器的应用
我们用 list 的正向迭代器生成 list 的反向迭代器为例。
8.2.1. ReverseIterator.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
namespace MyReverseIterator {
template<class Iterator, class Reference, class Pointer>
struct ReverseIterator {
typedef ReverseIterator<Iterator, Reference, Pointer> Self;
Iterator _it;
ReverseIterator(Iterator it) :_it(it) { };
Self& operator++() { --_it; return *this; }
Self& operator--() { ++_it; return *this; }
Reference operator*() {
Iterator temp = _it;
--temp;
return *temp;
}
Pointer operator->() { return &(operator*()); }
bool operator!=(const Self& rit) { return _it != rit._it; }
bool operator==(const Self& rit) { return _it == rit._it; }
};
};
8.2.2. List.h
#pragma once
#include <iostream>
using namespace std;
#include <assert.h>
#include <string>
#include "ReverseIterator.h"
namespace MyList {
template <class T>
struct ListNode {
ListNode* Previous;
ListNode* Next;
T Val;
ListNode(const T& val = T()) :Previous(nullptr) , Next(nullptr) , Val(val) { }
};
template <class T, class Reference, class Pointer>
struct ListIterator {
typedef ListNode<T> Node;
typedef ListIterator<T, Reference, Pointer> Self;
ListIterator(Node* node) :_Node(node) { };
Node* _Node;
Self& operator++() {
_Node = _Node->Next;
return *this;
}
Self ++() {
Self temp = *;
_Node = _Node->Next;
temp;
}
Self& --() {
_Node = _Node->Previous;
*;
}
Self --() {
Self temp = *;
_Node = _Node->Previous;
temp;
}
Reference *() { _Node->Val; }
Pointer ->() { &_Node->Val; }
!=( Self& it) {
_Node != it._Node;
}
==( Self& it) { _Node == it._Node; }
};
< >
{
ListNode<T> Node;
:
ListIterator<T, T&, T*> iterator;
ListIterator<T, T&, T*> const_iterator;
MyReverseIterator::ReverseIterator<iterator, T&, T*> reverse_iterator;
MyReverseIterator::ReverseIterator<iterator, T&, T*> const_reverse_iterator;
:
{
_Head = ();
_Head->Next = _Head;
_Head->Previous = _Head;
_Size = ;
}
() { (); }
( List<T>& lt) {
();
(& element : lt) {
(element);
}
}
{ (()); }
{ (()); }
{ (()); }
{ (()); }
{
(_Head->Next);
}
{
(_Head);
}
{
_Head->Next;
}
{
_Head;
}
{
((), Val);
}
{ (--()); }
{ ((), Val); }
{ (()); }
{
Node* NewNode = (Val);
Node* Current = Position._Node;
Node* Prev = Current->Previous;
Prev->Next = NewNode;
NewNode->Previous = Prev;
NewNode->Next = Current;
Current->Previous = NewNode;
_Size++;
}
{
Node* Current = Position._Node;
Node* Prev = Current->Previous;
Node* Next = Current->Next;
Prev->Next = Next;
Next->Previous = Prev;
Current;
_Size--;
(Next);
}
{ _Size; }
{ _Size == ; }
{
iterator it = ();
(it != ()) {
it = (it);
}
}
{
std::(_Head, lt._Head);
std::(_Size, lt._Size);
}
List<T>& =(List<T> lt) {
(lt);
*;
}
~() {
();
_Head;
_Head = ;
}
:
Node* _Head;
_Size;
};
}
8.2.3. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"
struct A {
int _a1, _a2;
A(int a, int b) :_a1(a), _a2(b) {}
};
void TestList12() {
MyList::List<int> lt;
lt.push_back(10);
lt.push_back(20);
lt.push_back(30);
lt.push_back(40);
lt.push_back(50);
MyList::List<int>::reverse_iterator rit = lt.rbegin();
while (rit != lt.rend()) {
cout << *rit << " ";
++rit;
}
cout << endl;
}
void TestList13() {
MyList::List<A> lt;
A aa1(1, 1);
lt.push_back(aa1);
lt.push_back(aa1);
lt.push_back(A(2, ));
lt.({ , });
lt.({ , });
MyList::List<A>::reverse_iterator rit = lt.();
(rit != lt.()) {
cout << rit->_a1 << << rit->_a2 << endl;
++rit;
}
cout << endl;
}
{
();
cout << endl;
();
;
}