目录
导读
我们上次了解了stack和queue的一些基本使用,今天我们就来试着实现一下。
1. stack和queue的底层
1.1 stack
C++中的stack通常是由deque(双端队列)或vector(动态数组)实现的。这是因为deque和vector都提供了方便的尾部插入和删除操作,符合stack的后进先出(LIFO)原则。
- 如果使用deque实现stack,那么每次将元素推入栈顶时,将元素插入到deque的尾部;而每次从栈顶弹出元素时,从deque的尾部删除元素。这样可以快速实现栈的操作,并保持栈的后进先出的特性。
- 如果使用vector实现stack,那么每次将元素推入栈顶时,将元素插入到vector的尾部;而每次从栈顶弹出元素时,从vector的尾部删除元素。类似地,这样也可以实现栈的操作,但由于vector是动态数组,所以在元素数量超过vector的当前容量时,可能需要重新分配内存并复制元素,这可能导致性能损失。
C++的标准库中还提供了适配器(adapter)stack,它基于deque或vector实现,隐藏了底层容器的实现细节,并提供了方便的栈操作接口。这样可以在不直接操作底层容器的情况下使用stack,使代码更加简洁和易读。
需要注意的是,由于stack只提供了后进先出的操作,没有提供随机访问元素的能力,因此不支持迭代器遍历。如果需要遍历栈中的元素,可以通过反复弹出栈顶元素并处理,直到栈为空。
1.2 queue
queue
是一个基于适配器模式实现的容器适配器。它是一个先进先出(FIFO)的数据结构,内部使用一个底层容器来存储元素。
默认情况下,queue
使用deque
作为其底层容器。deque
是一种双向队列,支持快速在头部和尾部进行插入和删除操作,因此适合作为queue
的底层容器。
通过适配器的方式,
queue
将底层容器的接口封装起来,提供了一组符合先进先出原则的操作。这使得使用queue
变得更加方便、简洁,并且可以灵活地选择底层容器类型,以满足特定的需求。如果需要自定义底层容器,可以通过模板参数来指定特定的容器类型。
2. 什么是适配器
适配器(Adapter)是一种设计模式,用于将一个类的接口转换为客户端所期望的另一个接口。适配器模式允许原本不兼容的类能够一起工作。
适配器通常有两种类型:
- 类适配器:使用多重继承的方式,将被适配类的接口转换为目标接口。适配器继承被适配类,并实现目标接口。
- 对象适配器:通过组合的方式,将适配器对象与被适配对象关联起来,将被适配类的接口转换为目标接口。适配器实现目标接口,并在内部持有被适配对象的实例。
适配器模式常用于以下情况:
- 当需要使用一个已经存在的类,但其接口与现有代码不兼容时,可以使用适配器模式将其接口转换为目标接口。
- 当需要复用一些现有类,但是这些类不具备与所需接口匹配的方法时,可以使用适配器模式添加额外的逻辑,实现所需接口。
总结来说,适配器模式可以让原本不兼容的类能够一起工作,实现接口转换和兼容性。
3. 常见适配器
以下是C++中的一些常见适配器:
-
stack:适配器stack用于将deque或vector等底层容器实现为后进先出(LIFO)的栈。
-
queue:适配器queue用于将deque或list等底层容器实现为先进先出(FIFO)的队列。
-
priority_queue:适配器priority_queue用于将vector或deque等底层容器实现为优先级队列,其中元素按照一定的优先级顺序排列。
-
stack和queue的适配器:C++标准库还提供了两个适配器,即stack和queue,它们可以使用deque、list或vector等底层容器作为实现。这样可以根据需求选择不同的底层容器,以满足不同的性能要求。
这些适配器通过封装底层容器的实现,提供了一种统一的接口,使得程序员可以方便地使用这些数据结构和算法,而不需要关心底层容器的具体实现细节。此外,适配器还提供了简化和抽象的功能,使得代码更加易读和可维护。
4. stack具体实现
4.1 成员变量
//第二个模板参数给一个缺省值(只能从右往左给)
template<class T, class Container = vector<T>>
class stack
{
private:
Container _con;
};
Container
表示用于存储栈元素的容器类型,默认情况下使用vector<T>
作为容器类型。可以通过实例化stack
类时指定具体的容器类型。通过使用模板类
stack
,可以实例化不同类型元素的栈,并且可以灵活选择底层容器类型。例如,可以创建一个存储整数类型的栈:stack<int>
,默认使用vector<int>
作为容器。也可以创建一个存储字符串类型的栈:stack<string, list<string>>
,使用list<string>
作为容器。
4.2 push函数
void push(const T& x)
{
_con.push_back(x);
}
具体来说,这个函数使用了
_con
容器的push_back()
成员函数,将元素x
插入到容器的尾部。每次调用
push()
函数,新元素都会被添加到底层容器的尾部,实现了栈的特性:后进先出(LIFO)。
4.3 pop函数
void pop()
{
_con.pop_back();
}
pop
函数使用了底层容器_con
的pop_back()
成员函数,它会从底层容器的末尾弹出一个元素。
4.4 size函数
size_t size()
{
return _con.size();
}
这个函数使用了
_con
容器的size()
成员函数,它会返回底层容器的元素个数。
4.5 empty函数
bool empty()
{
return _con.empty();
}
这是
stack
类中的empty
函数的实现。它执行的操作是判断底层容器是否为空。具体来说,这个函数使用了
_con
容器的empty()
成员函数,它会检查底层容器是否为空。如果底层容器为空,返回true
,否则返回false
。
4.6 top函数
const T& top()
{
return _con.back();
}
top
函数使用了底层容器_con
的back()
成员函数,它返回底层容器中的最后一个元素,即栈中的顶部元素。
5. stack实现完整代码
5.1 stack.h
#pragma once
#include <iostream>
using namespace std;
#include <vector>
namespace Mystack
{
//第二个模板参数给一个缺省值(只能从右往左给)
template<class T, class Container = vector<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con.back();
}
private:
Container _con;
};
}
5.2 test.cpp
#include "stack.h"
void test_stack()
{
Mystack::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;
}
int main()
{
test_stack();
return 0;
}
6. queue具体实现
6.1 成员变量
template<class T, class Container = deque<T>>
class queue
{
private:
Container _con;
};
这是一个queue类的模板定义。它使用一个模板参数T来表示元素的类型,并使用一个模板参数Container来表示底层容器的类型,默认为deque<T>。
在这个queue类中,私有成员变量_con表示底层容器,用于存储元素。它的类型是通过模板参数Container来确定的,默认情况下是deque<T>类型。
这个模板类定义了一个基本的队列数据结构,使得用户可以方便地在底层容器上进行队列操作,例如入队、出队、获取队头元素等。具体的队列操作实现可以通过对底层容器的操作来完成。
6.2 push函数
void push(const T& x)
{
_con.push_back(x);
}
这个函数使用了
_con
容器的push_back()
成员函数,将元素x
插入到容器的尾部。在deque
容器中,push_back()
函数的时间复杂度为常数时间,可以快速地将元素添加到容器的尾部。
6.3 pop函数
void pop()
{
_con.pop_front();
}
这个函数使用了
_con
容器的pop_front()
成员函数,它会删除底层容器的第一个元素。在deque
容器中,pop_front()
函数的时间复杂度为常数时间,因此可以快速地删除第一个元素。
6.4 size函数
size_t size()
{
return _con.size();
}
这是
queue
类中的size
函数的实现。它执行的操作是返回底层容器的元素个数。具体来说,这个函数使用了
_con
容器的size()
成员函数,它会返回底层容器的元素个数。在deque
容器中,size()
函数的时间复杂度为常数时间,因此可以快速地获取元素个数。
6.5 empty函数
bool empty()
{
return _con.empty();
}
这是
queue
类中的empty
函数的实现。它执行的操作是判断底层容器是否为空。具体来说,这个函数使用了
_con
容器的empty()
成员函数,它会检查底层容器是否为空。如果底层容器为空,返回true
,否则返回false
。
6.6 front和back函数
const T& front()
{
return _con.front();
}
const T& back()
{
return _con.back();
}
这是
queue
类中的front
和back
函数的实现。它们分别返回队列的第一个元素和最后一个元素的引用。具体来说,
front
函数使用了底层容器_con
的front()
成员函数,它会返回底层容器的第一个元素的引用。back
函数则使用了_con
的back()
成员函数,它返回底层容器的最后一个元素的引用。这样,可以通过调用
front()
和back()
函数来访问队列的第一个元素和最后一个元素,而不需要直接操作底层容器。
7. queue实现完整代码
7.1 queue.h
#pragma once
#include <iostream>
using namespace std;
#include<deque>
namespace Myqueue
{
template<class T, class Container = deque<T>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
const T& front()
{
return _con.front();
}
const T& back()
{
return _con.back();
}
private:
Container _con;
};
}
7.2 test.cpp
#include "queue.h"
void test_queue1()
{
Myqueue::queue<int> q;
q.push(1);
q.push(2);
cout << q.front() << " ";
q.pop();
q.push(3);
q.push(4);
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
cout << endl;
}
int main()
{
test_queue1();
return 0;
}