1.简单实现stack
构建一个模板,俩个参数,这里第一个一般是数据的类型,第二个是由什么来实现栈,在主函数里传了int和vector<int>,第二个不传参也可以,因为是缺省参数,默认为vector,这样就可以使用vector封装好的函数使用,push为尾插,就可以使用vector的push_back函数,pop是尾删,top是栈顶的元素,size是栈的大小,最后一个是判断是否为空。
cpp
namespace zym
{
template<class T, class Container = vector<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
const T& top() const
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
//test.cpp
#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
#include"Stack.h"
#include"Queue.h"
//#include"PriorityQueue.h"
int main()
{
//bit::stack<int, vector<int>> st;
//bit::stack<int, list<int>> st;
zym::stack<int, vector<int>> st;
// 类模板实例化时,按需实例化,使用哪些成员函数就实例化哪些,不会全实例化
st.push(1);
st.push(2);
st.push(3);
st.push(4);
cout << st.top() << endl;
st.pop();
zym::queue<int, list<int>> q;
//zym::queue<int> q;
//q.push(1);
//q.push(2);
//q.push(3);
//q.push(4);
//cout << q.front() << endl;
//q.pop();
return 0;
}
2.简单实现Queue
跟之前的stack基本是一样的,除了实现原理不一样,队列需要链表,剩下就是使用对应的库函数。
cpp
namespace zym
{
template<class T,class Container=list<int>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& front() const
{
return _con.front();
}
const T& back()
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
zym::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
cout << q.front() << endl;
q.pop();
3.deque
deque概念
deque就是把list和vector合在一起了,把这俩个互补了,vector因为是连续的存储空间,所以尾插和下标访问效率好,且缓存利用率也高,但数组需要扩容,扩容就会带来效率低和空间浪费,在中间位置插入数据效率低,因为要挪动数据。list则是按需申请和释放空间,不需要扩容,链表可以在任意位置插入和删除,缺点是不能像数组一样通过下标访问。而deque是结合俩者的优点,但是效率并不是很好。
deque的底层
链表是一个一个分散的空间,vector则是连续空间,则deque是分散的连续空间,list的每个节点只有一个元素的空间大小,而deque是每个节点变为了一个数组buf,用一个二级指针数字map储存每一个buf的地址,这个map是从中间作为第一个的,如果要头插就会在前面插入。
类里面有四个成员变量,都是迭代器,cur是指向当前元素的下一个,first是当前buf(数组)的第一个,last是最后一个元素的下一个,node是指向储存当前buf的位置。
移动下一个节点
重载的++是先判断cur和last是否一样,如果一样就调用set_node函数,set_node是把node+1赋值给node,first指向node+1的第一个位置,last等于first+这个数组的大小。
下标访问
如果是在第一个buf,要访问第n个,则可以先找到是在哪一个buf,n/sizeof(buf)表示是在第几个buf,然后是去找是在buf里面的第几个位置,n%sizeof(buf)表示是在buf里面的第几个位置,如果是在前面(头插后),则这样的办法就不太行,需要另外的,vs的编译器是把下标n和cur-first相加。
头插
注意的是头插的话,是从last位置开始的,这时候last就是最后一个元素的下一个,cur就是指向当前元素而不是下一个了。
插入和删除
如果在中间要插入和删除,都会挪动数据,就会有效率低的问题,如果是让buf的容量加减在插入和删除时,这样又会有问题,每个buf的大小不一样,在下标访问就需要更复杂的计算了,vs则是buf不变。
总结
1.deque头插尾插效率高
2.下标访问也还不错(计算以及判断多有影响)
3.中间插入删除效率很低,要挪动数据
4.用deque来实现stack和queue
注意:对于模板在没有按需实例化时,不会检查细节的代码,只会检查大概,所以可能运行没问题,后面有问题是因为有写函数没有调用,调用了编译器才会仔细检查代码的合理性。
queue:
cpp
namespace zym
{
template<class T,class Container=deque<int>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& front() const
{
return _con.front();
}
const T& back()
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
stack:
cpp
namespace zym
{
template<class T, class Container = deque<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
const T& top() const
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
5.priority_queue
priority_queue跟堆很像,堆是用来实现优先级队列的,所以底层还是堆的实现那些。
仿函数
这里的类Less和Greater里面只有一个成员函数,没有成员变量,但我们要建大堆和小堆时需要改变代码的大于号或者小于号,是不方便的,所以可以在priority_queue模板再加一个参数,来接受仿函数,需要小于就传Less,需要大于就传Greater,这样就可以一个代码实现大堆和小堆。
cpp
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
实现代码:
cpp
#pragma once
#include<vector>
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
namespace zym
{
template<class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue
{
public:
void AdjustUp(int child)
{
Compare com;
int parent = (child - 1) / 2;
while (child > 0)
{
if (com(_con[parent], _con[child]))
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
void push(const T & x)
{
_con.push_back(x);
AdjustUp(_con.size() - 1);
}
void AdjustDown(int parent)
{
size_t child = parent * 2 + 1;
Compare com;
while (child < _con.size())
{
if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
{
++child;
}
if (com(_con[parent], _con[child]))
{
swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
const T& top()
{
return _con[0];
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}