学习笔记,部分内容参考网络教学视频,习题来源Leetcode,侵删
队列
先进先出,后进后出
环形队列
依赖数组实现,但必须实现环形

两根指针,first指向队首,rear指向队尾,rear指向的是最后一个元素的下一个位置,也表示队内元素个数
使用数组实现线型队列,first以前的数组空间全部浪费,导致数组开多大都不够,所以选择环形
- 初始化:
first = rear = 0; - 入队:
arr[rear] = 10; rear = (rear + 1) % len); // 不是线型的 rear++; - 出队:
first++; - 判满判空:预留一个空位置给
rear,否则rear和first重合时无法分辨是满还是空- 空的:
first == rear - 满的:
(rear + 1) / len) == first
- 空的:
代码实现:
#include <iostream>
using namespace std;
class Queue
{
public:
Queue(int size = 10) : cap_(size),
front_(0),
back_(0),
size_(0)
{
pQueue_ = new int[cap_];
}
~Queue()
{
delete[] pQueue_;
pQueue_ = nullptr;
}
public:
// 入队
void push(int val)
{
if ((back_ + 1) % cap_ == front_) // 满了扩容
{
expand(2 * cap_);
}
pQueue_[back_] = val;
back_ = (back_ + 1) % cap_;
size_++;
}
// 出队
void pop()
{
if (front_ == back_)
{
throw "The queue is empty!";
}
front_ = (front_ + 1) % cap_;
size_--;
}
// 队首
int front() const
{
if (front_ == back_)
{
throw "The queue is empty!";
}
return pQueue_[front_];
}
// 队尾
int back() const
{
if (front_ == back_)
{
throw "The queue is empty!";
}
return pQueue_[(back_ - 1 + cap_) % cap_];
}
// 队空
bool empty() const
{
return front_ == back_;
}
// 队列元素个数
int size()
{
return size_; // O(1)
/*
或者 O(n) 遍历
int size = 0;
for(int i = front_; i != rear_; i = (i + 1) % cap_)
{
size++;
}
return size;
*/
}
private:
void expand(int size)
{
int *p = new int[size];
// 不能直接操作
// i遍历新数组,j遍历老数组
int i = 0;
int j = front_;
for (; j != back_; j = (j + 1) % cap_,i++)
{
p[i] = pQueue_[j];
}
delete[] pQueue_;
pQueue_ = p;
// 还有一些属性要修改
cap_ = size;
front_ = 0;
back_ = i;
}
private:
int *pQueue_;
int cap_; // 空间容量
int front_; // 队头
int back_; // 队尾
int size_;
};
int main()
{
try
{
// 测试基本功能
Queue q(3);
cout << "队列是否为空? " << (q.empty() ? "是" : "否") << endl; // 是
// 入队测试
q.push(10);
q.push(20);
q.push(30); // 触发扩容到6
cout << "入队后队列大小: " << q.size() << endl; // 3
cout << "队首元素: " << q.front() << endl; // 10
cout << "队尾元素: " << q.back() << endl; // 30
// 出队测试
q.pop();
cout << "出队后队首元素: " << q.front() << endl; // 20
cout << "出队后队尾元素: " << q.back() << endl; // 30
// 测试扩容后继续入队
q.push(40);
q.push(50);
q.push(60); // 容量6已满
q.push(70); // 触发扩容到12
cout << "扩容后队列大小: " << q.size() << endl; // 6
// 测试队空异常
Queue emptyQueue;
emptyQueue.pop(); // 应抛出异常
}
catch (const char* msg)
{
cout << "异常: " << msg << endl;
}
// 测试边界情况
try
{
Queue q;
q.push(1);
cout << "单元素队首: " << q.front() << endl; // 1
cout << "单元素队尾: " << q.back() << endl; // 1
q.pop();
cout << "队列是否为空? " << (q.empty() ? "是" : "否") << endl; // 是
}
catch (const char* msg)
{
cout << "异常: " << msg << endl;
}
return 0;
}
链式队列
依赖双向循环链表实现
#include <iostream>
using namespace std;
class LinkQueue
{
public:
LinkQueue() : size_(0)
{
head_ = new Node();
head_->next_ = head_;
head_->pre_ = head_;
}
~LinkQueue()
{
Node *p = head_->next_;
while (p != head_)
{
head_->next_ = p->next_;
p->next_->pre_ = head_;
delete p;
p = head_->next_;
}
delete head_;
head_ = nullptr;
}
public:
void push(int val)
{
Node *node = new Node(val);
node->next_ = head_;
node->pre_ = head_->pre_;
head_->pre_->next_ = node;
head_->pre_ = node;
size_++;
}
void pop()
{
if (empty()) // 添加边界检查
{
throw "The queue is empty!";
}
Node *p = head_->next_;
head_->next_ = p->next_;
p->next_->pre_ = head_;
delete p;
p = head_->next_;
size_--;
}
int front() const
{
if (head_->next_ == head_)
{
throw "The queue is empty!";
}
return head_->next_->data_;
}
int back() const
{
if (head_->next_ == head_)
{
throw "The queue is empty!";
}
return head_->pre_->data_;
}
bool empty() const
{
return head_->next_ == head_;
}
int size()
{
return size_;
}
private:
struct Node
{
Node(int data = 0) : data_(data),
next_(nullptr),
pre_(nullptr)
{
}
int data_;
Node *next_;
Node *pre_;
};
Node *head_;
int size_;
};
int main()
{
try
{
// 测试基本功能
LinkQueue q;
cout << "队列是否为空? " << (q.empty() ? "是" : "否") << endl; // 是
// 入队测试
q.push(10);
q.push(20);
q.push(30); // 触发扩容到6
cout << "入队后队列大小: " << q.size() << endl; // 3
cout << "队首元素: " << q.front() << endl; // 10
cout << "队尾元素: " << q.back() << endl; // 30
// 出队测试
q.pop();
cout << "出队后队首元素: " << q.front() << endl; // 20
cout << "出队后队尾元素: " << q.back() << endl; // 30
// 测试扩容后继续入队
q.push(40);
q.push(50);
q.push(60); // 容量6已满
q.push(70); // 触发扩容到12
cout << "扩容后队列大小: " << q.size() << endl; // 6
// 测试队空异常
LinkQueue emptyQueue;
emptyQueue.pop(); // 应抛出异常
}
catch (const char *msg)
{
cout << "异常: " << msg << endl;
}
// 测试边界情况
try
{
LinkQueue q;
q.push(1);
cout << "单元素队首: " << q.front() << endl; // 1
cout << "单元素队尾: " << q.back() << endl; // 1
q.pop();
cout << "队列是否为空? " << (q.empty() ? "是" : "否") << endl; // 是
}
catch (const char *msg)
{
cout << "异常: " << msg << endl;
}
return 0;
}
用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x)将元素 x 推到队列的末尾int pop()从队列的开头移除并返回元素int peek()返回队列开头的元素boolean empty()如果队列为空,返回true;否则,返回false
说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top,peek/pop from top,size, 和is empty操作是合法的。 - 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
提示:
1 <= x <= 9- 最多调用
100次push、pop、peek和empty - 假设所有操作都是有效的 (例如,一个空的队列不会调用
pop或者peek操作)
进阶:
- 你能否实现每个操作均摊时间复杂度为
O(1)的队列?换句话说,执行n个操作的总时间复杂度为O(n),即使其中一个操作可能花费较长时间。
算法

较为简单地想,拿出最后一个元素只需要把s1元素入栈到s2,s2栈首元素出栈就是队首出栈,剩下的元素再入栈回s1
但是pop,peek(查看队首)操作都会是时间复杂度为O(n),还可以继续优化
s1负责push,如果做pop和peek都从s2操作(如果s2是空的,就把s1元素都拿过来),判空empty则需要s1,s2均为空。
代码实现
#include <iostream>
#include <stack>
using namespace std;
class MyQueue
{
public:
MyQueue()
{
}
void push(int x)
{
s1.push(x);
}
int pop()
{
if (s2.empty())
{
while (!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
}
int val = s2.top();
s2.pop();
return val;
}
int peek()
{
if (s2.empty())
{
while (!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
}
return s2.top();
}
bool empty()
{
return s1.empty() && s2.empty();
}
private:
stack<int> s1;
stack<int> s2;
};
用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x)将元素 x 压入栈顶。int pop()移除并返回栈顶元素。int top()返回栈顶元素。boolean empty()如果栈是空的,返回true;否则,返回false。
注意:
- 你只能使用队列的标准操作 —— 也就是
push to back、peek/pop from front、size和is empty这些操作。 - 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
提示:
1 <= x <= 9- 最多调用
100次push、pop、top和empty - 每次调用
pop和top都保证栈不为空
**进阶:**你能否仅用一个队列来实现栈。
基础算法

- 两个队列分工:
- 一个主队列
q1用于存储栈的元素(非空)。 - 一个辅助队列
q2始终为空,仅在出栈时临时使用。
- 一个主队列
- 入栈(
push):
直接将元素加入主队列q1(时间复杂度 O (1))。 - 出栈(
pop):- 将
q1中除最后一个元素外的所有元素转移到q2。 - 弹出
q1中剩余的最后一个元素(即栈顶元素)。 - 交换
q1和q2的角色(q2变为新的主队列)。
(时间复杂度 O (n),n 为当前元素个数)
- 将
- 取栈顶(
top):
逻辑与pop类似,但转移元素后不弹出最后一个元素,而是记录其值后放回(或直接返回队列尾部元素)。 - 判空(
empty):
主队列q1为空时,栈为空。
代码实现
#include <iostream>
#include <queue>
using namespace std;
class MyStack
{
private:
queue<int> q1; // 主队列(存储元素)
queue<int> q2; // 辅助队列(临时使用)
public:
MyStack() {}
void push(int x)
{
q1.push(x); // 直接加入主队列
}
int pop()
{
// 将 q1 中前 n-1 个元素转移到 q2
while (q1.size() > 1)
{
q2.push(q1.front());
q1.pop();
}
// q1 中剩余的最后一个元素是栈顶
int topVal = q1.front();
q1.pop();
// 交换 q1 和 q2,使 q2 恢复为空
swap(q1, q2);
return topVal;
}
int top()
{
// 复用 pop 的逻辑,但保留最后一个元素
while (q1.size() > 1)
{
q2.push(q1.front());
q1.pop();
}
int topVal = q1.front(); // 记录栈顶值
// 将最后一个元素也转移到 q2,保持 q1 为空
q2.push(q1.front());
q1.pop();
swap(q1, q2); // 恢复 q1 为主队列
return topVal;
}
bool empty()
{
return q1.empty(); // 主队列空则栈空
}
};
进阶算法

只需一个队列即可实现,核心思路是:入栈后将队列中前 n-1 个元素重新入队,使新入栈的元素位于队首(模拟栈顶)。
#include <iostream>
#include <queue>
using namespace std;
class MyStack
{
private:
queue<int> q;
public:
MyStack() {}
void push(int x)
{
q.push(x);
// 将前 n-1 个元素重新入队,使 x 位于队首
for (int i = 0; i < q.size() - 1; ++i)
{
q.push(q.front());
q.pop();
}
}
int pop()
{
int topVal = q.front();
q.pop();
return topVal;
}
int top()
{
return q.front();
}
bool empty()
{
return q.empty();
}
};
队列(循环队列、链式队列、两个栈实现一个队列、两个队列实现一个栈)&spm=1001.2101.3001.5002&articleId=154202787&d=1&t=3&u=c13eca9046e345debf7a72b832cfbb0c)
5158

被折叠的 条评论
为什么被折叠?



