1. stack
模拟实现
C++STL
中的栈是一种容器适配器,它是将vector/list
进行封装,push/pop
等接口直接调用vector/list
的接口即可,不需要像C语言那样,从头开始造轮子
namespace byh
{
template<class T, class Container = deque<T>>
class stack
{
public:
void push(const T& val)
{
_con.push_back(val);
}
void pop()
{
_con.pop_back();
}
const T& top()
{
return _con.back();
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}
int main()
{
byh::stack<int> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
cout << endl;
return 0;
}
在模板实例化时,我们给栈传什么容器,它就调用该容器的接口,默认容器是deque
deque
是一种双端队列,它结合了vector
和list
的优势
vector
:
- 优势:能够随机访问
- 劣势:头插/删,中间插/删要挪动数据,效率低;扩容有消耗
list
:
- 优势:任意位置插入/删除效率高
- 劣势:不支持随机访问
可以说,vector
和list
是两个互补的容器,既然这样,能不能把这两个容器的优势结合;因为有这样的需求,就有了deque
它的底层会有一个中控指针数组,每个元素都指向一个长度为N的数组
尾插时,如果当前的所有数组都满了,则开辟新的指针,指向新的数组,插入元素
头插时,在数组中,以从后往前的顺序依次插入元素
访问第i个元素时,如果第一个数组没满,i减去第一个数组元素个数,进行下面的运算:
- i / N -->找到该元素在第几个数组
- i % N -->找到是在该数组的第几个元素
如果第一个数组满了,直接进行上面的运算
如果中控指针数组满了,进行扩容,虽然最总还是要扩容,但消耗上大大降低
deque
:
- 优点:头插/删,尾插/删效率都不错
- 缺点:中间插/删会比较麻烦,不如
list
;随机访问的效率不如vector
stack
和queue
的默认容器使用deque
的原因就在于它的头插/删,尾插/删效率可以,并且不会使用随机访问或者中间插/删
2.queue
模拟实现
namespace byh
{
template<class T, class Container = deque<T>>
class queue
{
public:
void push(const T& val)
{
_con.push_back(val);
}
void pop()
{
_con.pop_front();
}
T& back()
{
return _con.back();
}
T& front()
{
return _con.front();
}
const T& back() const
{
return _con.back();
}
const T& front() const
{
return _con.front();
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}
int main()
{
byh::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
cout << endl;
return 0;
}
3. priority_queue
queue
系列中,除了先进先出的普通queue
之外,还有一个优先级队列priority_queue
pop
时,不再是先进先出,而是按规定的优先级取出元素
#include<iostream>
#include<queue>
int main()
{
std::priority_queue<int> pq;
pq.push(3);
pq.push(2);
pq.push(4);
pq.push(5);
pq.push(1);
while (!pq.empty())
{
std::cout << pq.top() << " ";
pq.pop();
}
std::cout << std::endl;
return 0;
}
// 输出 5 4 3 2 1
仿函数实际上是一个类,只不过我们可以用函数的方式去使用它
它的内部是对()
运算符的重载
template<class T>
struct Less
{
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
void Test()
{
byh::Less<int> less;
cout << less(1, 2) << endl;// 有名对象
cout << less.operator()(1, 2) << endl;
cout << byh::Less<int>()(1, 2) << endl;// 匿名对象
}
我们可以在仿函数内部控制比较的逻辑,生活中大部分场景下都是用自定义类型描述一个对象,如何控制该对象的比较,进行排序,就需要我们规定好比较方式
struct Goods
{
Goods(const char* str, double price, int evaluate)
:_name(str)
,_price(price)
,_evaluate(evaluate)
{}
string _name;
double _price;
int _evaluate;
};
struct ComparePriceLess
{
bool operator()(const Goods& x, const Goods& y)
{
return x._price < y._price;
}
};
//...
int main()
{
vector<Goods> v = { {"苹果",3.1,2},{"草莓",4.2,4},{"苹果",2.6,3} };
sort(v.begin(), v.end(), ComparePriceLess());
return 0;
}
3.1 模拟实现
template<class T>
struct Less
{
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
struct Greater
{
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template<class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue
{
public:
void AdjustUp(int child)
{
int parent = (child - 1) / 2;
Compare com;
while (child > 0)
{
if (com(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else break;
}
}
void push(const T& val)
{
_con.push_back(val);
AdjustUp(size() - 1);
}
void AdjustDown(int parent)
{
int child = parent * 2 + 1;
Compare com;
while (child < size())
{
if (child + 1 < size() && com(_con[child], _con[child + 1]))
child++;
if (com(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else break;
}
}
void pop()
{
swap(_con[0], _con[size() - 1]);
_con.pop_back();
AdjustDown(0);
}
T& top()
{
return _con[0];
}
const T& top() const
{
return _con[0];
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
4. Reverse_Iterator
反向迭代器与正向迭代器功能类似,它们都是遍历,不同的是,反向迭代器++
往前走,--
往后走
我们可以对正向迭代器的++
和--
修改,但这样做,每个容器的迭代器都要进行拷贝修改,不仅代码冗余,还麻烦
于是,我们用const
迭代器的思路,将Reverse_Iterator
写成一个模板,我们给它传哪个容器的迭代器,编译器就帮我们形成哪个容器的反向迭代器,从而减少我们的工作量
STL
中,rbegin()
和rend()
的位置与begin()
和end()
相反
访问时,是解引用当前位置的上一个位置
namespace byh
{
template<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{
Iterator _it;
ReverseIterator(Iterator it)
:_it(it)
{}
Ref operator*()
{
Iterator temp = _it;
return *(--temp);
}
Ptr operator->()
{
return &(operator*());
}
// ++it
ReverseIterator operator++()
{
_it--;
return *this;
}
// --it
ReverseIterator operator--()
{
_it++;
return *this;
}
bool operator!=(ReverseIterator rit)
{
return _it != rit._it;
}
};
}
erator it)
:_it(it)
{}
Ref operator*()
{
Iterator temp = _it;
return *(--temp);
}
Ptr operator->()
{
return &(operator*());
}
// ++it
ReverseIterator operator++()
{
_it--;
return *this;
}
// --it
ReverseIterator operator--()
{
_it++;
return *this;
}
bool operator!=(ReverseIterator rit)
{
return _it != rit._it;
}
};
}