目录
队列和栈对比
队列queue
基本知识
一、基于数组的循环队列(循环队列)
例1:简单队列操作
例2:循环队列的C++实现
栈stack
基本知识
1. 基于数组的栈
2. 基于单链表的栈
两个栈stack实现一个队列queue
两个队列queue实现一个栈stack
优先队列 priority_queue
基本内容
头文件 #include
和队列基本操作相同:
定义
大顶堆与小顶堆
pair类型,先比较第一个元素,第一个相等比较第二个
自定义优先级
双端队列deque
操作图示:
deque的特点:
最好采用deque的情形:
deque的操作函数 博客园-deque
定义函数
容量函数
添加函数
删除函数
访问函数
其他函数
迭代器与算法
1. 迭代器
2. 算法
遍历元素
元素翻转
元素排序
双向链表List
基本知识
基本操作
容量函数
添加函数
删除元素
其他函数
迭代器与算法
迭代器
算法
遍历
元素翻转
元素排列
总结
队列:
q.empty() 如果队列为空返回true,否则返回false
q.size() 返回队列中元素的个数
q.pop() 删除队列首元素但不返回其值
q.front() 返回队首元素的值,但不删除该元素
q.push() 在队尾压入新元素
q.back() 返回队列尾元素的值,但不删除该元素
栈:
s.empty(); //如果栈为空则返回true, 否则返回false;
s.size(); //返回栈中元素的个数
s.top(); //返回栈顶元素, 但不删除该元素
s.pop(); //弹出栈顶元素, 但不返回其值
s.push(); //将元素压入栈顶
1、 先进先出
2、在队尾添加元素,在队头删除元素。
3、C++队列queue模板类的定义在
那么我们如何判断队列是空队列还是已满呢?
a、栈空: 队首标志=队尾标志时,表示栈空。
b、栈满 : 队尾+1 = 队首时,表示栈满。
使用标准库的队列时, 应包含相关头文件,在栈中应包含头文件: #include< queue> 。
定义:queue< int > q;
q.empty() 如果队列为空返回true,否则返回false
q.size() 返回队列中元素的个数
q.pop() 删除队列首元素但不返回其值
q.front() 返回队首元素的值,但不删除该元素
q.push() 在队尾压入新元素
q.back() 返回队列尾元素的值,但不删除该元素
以数组作为底层数据结构时,一般讲队列实现为循环队列。这是因为队列在顺序存储上的不足:每次从数组头部删除元素(出队)后,需要将头部以后的所有元素往前移动一个位置,这是一个时间复杂度为O(n)的操作。
循环队列的抽象数据类型:
template
class LoopQueue
{
public:
LoopQueue(int c = 10);
~LoopQueue(); //析构函数
public:
bool isEmpty(); //队列的判空
int size(); //队列的大小
bool push(T t); //入队列
bool pop(); //出队列
T front(); //队首元素
private:
int capacity;
int begin;
int end;
T* queue;
};
定义front为队列头元素的位置,rear为队列尾元素的位置,MAXSIZE为循环队列的最大长度。注意以下几点,循环队列迎刃而解:
#include
#include
using namespace std;
int main(){
queue q;
for (int i = 0; i < 10; i++){
q.push(i);
}
if (!q.empty()){
cout << "队列q非空!" << endl;
cout << "q中有" << q.size() << "个元素" << endl;
}
cout << "队头元素为:" << q.front() << endl;
cout << "队尾元素为:" << q.back() << endl;
for (int j = 0; j < 10; j++){
int tmp = q.front();
cout << tmp << " ";
q.pop();
}
cout << endl;
if (!q.empty()){
cout << "队列非空!" << endl;
}
system("pause");
return 0;
}
C++数据结构——队列_GeekZW的博客-CSDN博客_c++队列
#include
#include
#include
using namespace std;
template
class LoopQueue
{
public:
LoopQueue(int c = 10);
~LoopQueue();
bool isEmpty(); //队列的判空
int size(); //队列的大小
bool push(T t); //入队列
bool pop(); //出队列
T front(); //队首元素
private:
int capacity;
int begin;
int end;
T* queue;
};
template
LoopQueue::LoopQueue(int c = 10)
:capacity(c), begin(0), end(0), queue(nullptr)
{
queue = new T[capacity];
};
template
LoopQueue::~LoopQueue()
{
delete[]queue;
}
template
bool LoopQueue::isEmpty() //判断循环队列是否为空
{
if (begin == end)
return true;
return false;
};
template
int LoopQueue::size()
{
return (end - begin + capacity) % capacity; //计算循环队列的长度
};
template
bool LoopQueue::push(T t)
{
if (end + 1 % capacity == begin) //判断队列是否已满
{
return false;
}
queue[end] = t;
end = (end + 1) % capacity;
return true;
};
template
bool LoopQueue::pop() //判断队列是否为空
{
if (end == begin)
{
return false;
}
begin = (begin + 1) % capacity;
return true;
};
template
T LoopQueue::front()
{
if (end == begin)
{
return false;
}
return queue[begin];
};
int main()
{
LoopQueue queue(6);
queue.push("one");
queue.push("two");
queue.push("three");
queue.push("four");
queue.push("five");
cout << "队列长度" << queue.size() << endl;
while (!queue.isEmpty())
{
cout << queue.front() << endl;
queue.pop();
}
getchar();
//system("pause");
return 0;
}
C++数据结构——栈_GeekZW的博客-CSDN博客_c++栈
1、先进后出
2、允许元素插入与删除的一端称为栈顶,另一端称为栈底
pop push
3、 在使用标准库的栈时,应该使用头文件: #include< stack > ,若定义 stack
s.empty(); //如果栈为空则返回true, 否则返回false;
s.size(); //返回栈中元素的个数
s.top(); //返回栈顶元素, 但不删除该元素
s.pop(); //弹出栈顶元素, 但不返回其值
s.push(); //将元素压入栈顶
以数组为底层数据结构时,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向。
#include
#include
using namespace std;
int main()
{
stack mystack;
int sum = 0;
for (int i = 0; i <= 10; i++){
mystack.push(i);
}
cout << "size is " << mystack.size() << endl;
while (!mystack.empty()){
cout << " " << mystack.top();
mystack.pop();
}
cout << endl;
system("pause");
return 0;
}
//size is 11
// 10 9 8 7 6 5 4 3 2 1 0
以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的新节点将一直出现在链表的头部。
#include
using namespace std;
templateclass Stack
{
private:
struct Node
{
T data;
Node *next;
};
Node *head;
Node *p;
int length;
public:
Stack()
{
head = NULL;
length = 0;
}
void push(T n)//入栈
{
Node *q = new Node;
q->data = n;
if (head == NULL)
{
q->next = head;
head = q;
p = q;
}
else
{
q->next = p;
p = q;
}
length++;
}
T pop()//出栈并且将出栈的元素返回
{
if (length <= 0)
{
abort();
}
Node *q;
T data;
q = p;
data = p->data;
p = p->next;
delete(q);
length--;
return data;
}
int size()//返回元素个数
{
return length;
}
T top()//返回栈顶元素
{
return p->data;
}
bool isEmpty()//判断栈是不是空的
{
if (length == 0)
{
return true;
}
else
{
return false;
}
}
void clear()//清空栈中的所有元素
{
while (length > 0)
{
pop();
}
}
};
int main()
{
Stack s;
s.push('a');
s.push('b');
s.push('c');
while (!s.isEmpty())
{
cout << s.pop() << endl;
}
system("pause");
return 0;
}
队列的特性是 FIFOFIFO(先入先出),而栈的特性是 FILOFILO(先入后出)。
知道两者特性之后,我们需要用两个栈来模拟队列的特性,一个栈为入队栈,一个栈为出对栈。
当出队栈存在内容时,出队栈的栈顶,即为第一个出队的元素。
若出队栈无元素,我们的需求又是出队的话,我们就需要将入队栈的内容反序导入出队栈,然后弹出栈顶即可。
注意:根据栈的的特性,我们仅能使用 pushpush 和 poppop 操作。
leetcode-232-解答
class MyQueue {
private:
stack inStack, outStack;
void in2out() {
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
}
public:
MyQueue() {}
void push(int x) {
inStack.push(x);
}
int pop() {
if (outStack.empty()) {
in2out();
}
int x = outStack.top();
outStack.pop();
return x;
}
int peek() {
if (outStack.empty()) {
in2out();
}
return outStack.top();
}
bool empty() {
return inStack.empty() && outStack.empty();
}
};
作者:demigodliu
链接:https://leetcode-cn.com/problems/implement-queue-using-stacks/solution/tu-jie-guan-fang-tui-jian-ti-jie-yong-zh-4hru/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一个队列为主队列,一个为辅助队列,当入栈操作时,我们先将主队列内容导入辅助队列,然后将入栈元素放入主队列队头位置,再将辅助队列内容,依次添加进主队列即可。
class MyStack {
public:
queue queue1;
queue queue2;
MyStack() {
}
void push(int x) {
queue2.push(x);
while (!queue1.empty()) {
queue2.push(queue1.front());
queue1.pop();
}
swap(queue1, queue2);
}
int pop() {
int r = queue1.front();
queue1.pop();
return r;
}
int top() {
int r = queue1.front();
return r;
}
bool empty() {
return queue1.empty();
}
};
作者:demigodliu
链接:https://leetcode-cn.com/problems/implement-stack-using-queues/solution/wu-tu-guan-fang-tui-jian-ti-jie-yong-dui-63d4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
priority_queue< type, container, function >
后两个参数不可省略
在STL中,默认情况下(不加后面两个参数)是以vector为容器,以 operator< 为比较方式,所以在只使用第一个参数时,优先队列默认是一个最大堆,每次输出的堆顶元素是此时堆中的最大元素。
1. 大顶堆
//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue big_heap;
//另一种构建大顶堆的方法
priority_queue,less > big_heap2;
#include
#include
using namespace std;
int main(){
priority_queue p;
p.push(13356);
p.push(23);
p.push(85);
p.push(536);
p.push(43);
for(int i=0;i<5;i++){
cout<
2. 小顶堆
//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue,greater > small_heap;
#include
#include
using namespace std;
int main(){
priority_queue, greater >p;
p.push(13356);
p.push(23);
p.push(85);
p.push(536);
p.push(43);
for(int i=0;i<5;i++){
cout<
#include
#include
#include
using namespace std;
int main()
{
priority_queue > a;
pair b(68, 2);
pair c(167, 3);
pair d(268, 5);
a.push(d);
a.push(c);
a.push(b);
while (!a.empty())
{
cout << a.top().first << ' ' << a.top().second << '\n';
a.pop();
}
}
#include
#include
#include
using namespace std;
struct Node{
int x,y;
Node(int a, int b):
x(a), y(b) {}
};
struct cmp{
bool operator()(Node a, Node b){
if(a.x == b.x) return a.y>b.y;
return a.x>b.x;
}
};
int main(){
priority_queue, cmp>p;
for(int i=0; i<10; ++i)
p.push(Node(rand(), rand()));
while(!p.empty()){
cout<
1、支持随机访问,即支持[]以及at(),但是性能没有vector好。
2、可以在内部进行插入和删除操作,但性能不及list。
1、需要在两端插入和删除元素。
2、无需引用容器内的元素。
3、要求容器释放不再使用的元素。
deque a; // 定义一个int类型的双端队列a
deque a(10); // 定义一个int类型的双端队列a,并设置初始大小为10
deque a(10, 1); // 定义一个int类型的双端队列a,并设置初始大小为10且初始值都为1
deque b(a); // 定义并用双端队列a初始化双端队列b
deque b(a.begin(), a.begin()+3); // 将双端队列a中从第0个到第2个(共3个)作为双端队列b的初始值
除此之外,还可以直接使用数组来初始化向量:
int n[] = { 1, 2, 3, 4, 5 };
// 将数组n的前5个元素作为双端队列a的初值
// 说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,
// 这个主要是为了和deque.end()指针统一。
deque a(n, n + 5);
deque a(&n[1], &n[4]); // 将n[1]、n[2]、n[3]作为双端队列a的初值
deq.size(); 容器大小
deq.max_size(); 容器最大容量
deq.resize(); 更改容器大小:
deq.empty(); 容器判空:
deq.shrink_to_fit(); 减少容器大小到满足元素所占存储空间的大小:
deq.push_front(const T& x); //头部添加元素
deq.push_back(const T& x); //末尾添加元素
deq.insert(iterator it, const T& x); //任意位置插入一个元素
deq.insert(iterator it, int n, const T& x); 任意位置插入 n 个相同元素
deq.insert(iterator it, iterator first, iterator last); //插入另一个向量的 [forst,last] 间的数据
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
deque deq;
// 头部增加元素
deq.push_front(4);
// 末尾添加元素
deq.push_back(5);
// 任意位置插入一个元素
deque::iterator it = deq.begin();
deq.insert(it, 2);
// 任意位置插入n个相同元素
it = deq.begin(); // 必须要有这句
deq.insert(it, 3, 9);
// 插入另一个向量的[forst,last]间的数据
deque deq2(5,8);
it = deq.begin(); // 必须要有这句
deq.insert(it, deq2.end() - 1, deq2.end());
// 遍历显示
for (it = deq.begin(); it != deq.end(); it++)
cout << *it << " "; // 输出:8 9 9 9 2 4 5
cout << endl;
return 0;
}
deq.pop_front(); 头部删除元素:
deq.pop_back(); 末尾删除元素:
deq.erase(iterator it); 任意位置删除一个元素:
deq.erase(iterator first, iterator last); 删除 [first,last] 之间的元素:
deq.clear(); 清空所有元素:
下标访问:deq[1]; 并不会检查是否越界
at 方法访问:deq.at(1); 以上两者的区别就是 at 会检查是否越界,是则抛出 out of range 异常
访问第一个元素:deq.front();
访问最后一个元素:deq.back();
多个元素赋值:deq.assign(int nSize, const T& x); 类似于初始化时用数组进行赋值
交换两个同类型容器的元素:swap(deque&);
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
// 多个元素赋值
deque deq;
deq.assign(3, 1);
deque deq2;
deq2.assign(3, 2);
// 交换两个容器的元素
deq.swap(deq2);
// 遍历显示
cout << "deq: ";
for (int i = 0; i < deq.size(); i++)
cout << deq[i] << " "; // 输出:2 2 2
cout << endl;
// 遍历显示
cout << "deq2: ";
for (int i = 0; i < deq2.size(); i++)
cout << deq2[i] << " "; // 输出:1 1 1
cout << endl;
return 0;
}
deq.begin();
deq.end();
// 指向最后一个元素的下一个位置deq.cbegin();
// 意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。deq.cend();
deq.rbegin();
deq.rend();
deque::iterator it;
for (it = deq.begin(); it != deq.end(); it++)
cout << *it << endl;
// 或者
for (int i = 0; i < deq.size(); i++) {
cout << deq.at(i) << endl;
}
#include
reverse(deq.begin(), deq.end());
#include
sort(deq.begin(), deq.end()); // 采用的是从小到大的排序
// 如果想从大到小排序,可以采用先排序后反转的方式,也可以采用下面方法:
// 自定义从大到小的比较器,用来改变排序方式
bool Comp(const int& a, const int& b) {
return a > b;
}
sort(deq.begin(), deq.end(), Comp);
可以看到,deque 与 vector 的用法基本一致,除了以下几处不同:
其他均与vector相同。
注意:
1、除了at()函数,其他成员函数都不会检查索引或迭代器是否有效。2、元素的插入和删除可能会导致内存重新分配。所以任何插入或删除操作都会使所有指向deque元素的pointers、reference、iterators失效。唯一例外的是在首尾插入元素之后,pointers和reference可能仍然有效。
list a; // 定义一个int类型的列表a
list a(10); // 定义一个int类型的列表a,并设置初始大小为10
list a(10, 1); // 定义一个int类型的列表a,并设置初始大小为10且初始值都为1
list b(a); // 定义并用列表a初始化列表b
deque b(a.begin(), ++a.end()); // 将列表a中的第1个元素作为列表b的初始值
int n[] = { 1, 2, 3, 4, 5 };
list a(n, n + 5); // 将数组n的前5个元素作为列表a的初值
lst.size();
lst.max_size();
lst.resize(n);
lst.empty();
lst.push_front(const T& x);
lst.push_back(const T& x);
lst.insert(iterator it, const T& x);
lst.insert(iterator it, int n, const T& x);
lst.insert(iterator it, iterator first, iterator last);
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
list lst;
// 头部增加元素
lst.push_front(4);
// 末尾添加元素
lst.push_back(5);
// 任意位置插入一个元素
list::iterator it = lst.begin();
lst.insert(it, 2);
// 任意位置插入n个相同元素
lst.insert(lst.begin(), 3, 9);
// 插入另一个向量的[forst,last]间的数据
list lst2(5, 8);
lst.insert(lst.begin(), lst2.begin(), ++lst2.begin());
// 遍历显示
for (it = lst.begin(); it != lst.end(); it++)
cout << *it << " "; // 输出:8 9 9 9 2 4 5
cout << endl;
return 0;
}
lst.pop_front();
lst.pop_back();
lst.erase(iterator it);
lst.erase(iterator first, iterator last);
lst.clear();
lst.assign(int nSize, const T& x); // 类似于初始化时用数组进行赋值
swap(list&, list&); 或 lst.swap(list&);
lst.merge();
lst.splice(iterator it, list&);
lst.unique();
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
// 多个元素赋值s
list lst1;
lst1.assign(3, 1);
list lst2;
lst2.assign(3, 2);
// 交换两个容器的元素
// swap(lst1, lst2); // ok
lst1.swap(lst2);
// 遍历显示
cout << "交换后的lst1: ";
list::iterator it;
for (it = lst1.begin(); it!=lst1.end(); it++)
cout << *it << " "; // 输出:2 2 2
cout << endl;
// 遍历显示
cout << "交换后的lst2: ";
for (it = lst2.begin(); it != lst2.end(); it++)
cout << *it << " "; // 输出:1 1 1
cout << endl;
list lst3;
lst3.assign(3, 3);
list lst4;
lst4.assign(3, 4);
// 合并两个列表的元素
lst4.merge(lst3); // 不是简单的拼接,而是会升序排列
cout << "合并后的lst4: ";
for (it = lst4.begin(); it != lst4.end(); it++)
cout << *it << " "; // 输出:3 3 3 4 4 4
cout << endl;
list lst5;
lst5.assign(3, 5);
list lst6;
lst6.assign(3, 6);
// 在lst6的第2个元素处,拼接入lst5
lst6.splice(++lst6.begin(), lst5);
cout << "拼接后的lst6: ";
for (it = lst6.begin(); it != lst6.end(); it++)
cout << *it << " "; // 输出:6 5 5 5 6 6
cout << endl;
// 删除容器中相邻的重复元素
list lst7;
lst7.push_back(1);
lst7.push_back(1);
lst7.push_back(2);
lst7.push_back(2);
lst7.push_back(3);
lst7.push_back(2);
lst7.unique();
cout << "删除容器中相邻的重复元素后的lst7: ";
for (it = lst7.begin(); it != lst7.end(); it++)
cout << *it << " "; // 输出:1 2 3 2
cout << endl;
return 0;
}
lst.begin();
lst.end();
// 指向最后一个元素的下一个位置lst.cbegin();
// 意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。lst.cend();
lst.rbegin();
lst.rend();
list::iterator it;
for (it = lst.begin(); it != lst.end(); it++)
cout << *it << endl;
#include
reverse(lst.begin(), lst.end());
#include
sort(lst.begin(), lst.end()); // 采用的是从小到大的排序
// 如果想从大到小排序,可以采用先排序后反转的方式,也可以采用下面方法:
// 自定义从大到小的比较器,用来改变排序方式
bool Comp(const int& a, const int& b)
{
return a > b;
}
sort(lst.begin(), lst.end(), Comp);
可以看到,list 与 vector、deque 的用法基本一致,除了以下几处不同:
it+=i
;