Tag
【设计类】【队列】【数组】【2023-11-28】
题目来源
![](https://i-blog.csdnimg.cn/blog_migrate/92ecc7a1f15d2b32180b2d99b89fa8a9.png)
题目解读
设计一个队列,可以实在在前、中、后三个位置的 push 和 pop 操作。
解题思路
维护一个数组,在数组中使用指针指向前、后两个位置并维护两个的指向。本题还可以使用 vector
容器,可以省去移动的操作,这里没有介绍该方法,读者可以自行实现。
方法一:双指针
初始化
FrontMiddleBackQueue() {
front = CENTER;
rear = front - 1;
}
使用两个指针 front
和 rear
分别指向数组 data
的 “前后” 两个位置。初始化 data
数组,int data[5000]
,接着初始化 front = 2500
,rear = front - 1
。
getCount
int getCont(){
return rear - front + 1;
}
push 和 pop 操作
前、后两个位置的 push
和 pop
操作很容易实现:
void pushFront(int val) {
data[--front] = val;
}
void pushBack(int val) {
data[++rear] = val;
}
在 pushFront
之前先对 front
指针 --
,然后将 val
插入到该位置。在 pushBack
中需要先 ++ rear
,然后插入 val
。
int popFront() {
if(getCont() == 0){
return -1;
}
int val = data[front++]; // 弹出Front之后front变为前插下个数的后一个位置
return val;
}
int popBack() {
if(getCont() == 0){
return -1;
}
int val = data[rear--]; // 弹出Back之后rear变为尾插下个数的前一个位置
return val;
}
pop
操作之前需要先判断 data
是否为空,如是则返回 -1
;否则弹出对应的元素并更改指针为 “下一个” 元素。
pushMiddle
记此时数组 data
的长度为 cnt
,我们需要根据 cnt
的不同对 pushMiddle
进行分情况讨论:
- 当
cnt = 1
时,此时的 middle 位置为font - 1
,记为pushPos = font - 1
; - 否则,
pushPos = font + (cnt / 2)
;
接着需要将 val
插入到 pushPos
位置,如果 cnt = 1
,可以直接 pushFront(val)
;否则不能直接在 pushPos
插入 val
,需要先将 pushPos
及之后的元素依次向后移动一位,将 pushPos
位置空出,然后将 val
插入到 pushPos
位置,或者将pushPos
及之前的元素依次向前移动一位,将 pushPos
位置空出,然后将 val
插入到 pushPos
位置。
以下是后移的示例代码:
void pushMiddle(int val) {
int cnt = getCont();
if(cnt == 1){
pushFront(val);
return;
}
int pushPos = front+(cnt/2);
for(int i = rear+1; i > pushPos; --i){
data[i] = data[i-1];
}
data[pushPos] = val;
++rear;
}
popMiddle
pop
之前需要先判断数组是否为空,如是直接返回 -1
,否则需要先找到 popPos
即需要弹出的位置,由数学归纳知 popPos = front + (n - 1) / 2
,n
为数组此时的长度。
弹出 popPos
位置的元素之后,需要将 popPos
之后的元素依次左移一位填充空格,然后 --rear
。示例代码如下:
int popMiddle() {
int cnt = getCont();
if(cnt == 0){
return -1;
}
int popPos = front + (cnt - 1)/2;
int tmp = data[popPos];
for(int i = popPos; i < rear; ++i){
data[i] = data[i+1];
}
--rear;
return tmp;
}
实现代码
class FrontMiddleBackQueue {
#define CENTER 2500
int data[5000];
int front, rear;
public:
FrontMiddleBackQueue() {
front = CENTER;
rear = front - 1;
}
int getCont(){
return rear - front + 1;
}
void pushFront(int val) {
data[--front] = val;
}
/*
个数 插入位置
X front-1 特殊情况
XX front+1
XXX front+1
XXXX front+2
... ...
n个数 front+(n/2)
*/
void pushMiddle(int val) {
int cnt = getCont();
if(cnt == 1){
pushFront(val);
return;
}
int pushPos = front+(cnt/2);
for(int i = rear+1; i > pushPos; --i){
data[i] = data[i-1];
}
data[pushPos] = val;
++rear;
}
void pushBack(int val) {
data[++rear] = val;
}
int popFront() {
if(getCont() == 0){
return -1;
}
int val = data[front++]; // 弹出Front之后front变为前插下个数的后一个位置
return val;
}
/*
个数 pop位置
X front 特殊情况
XX front
XXX front+1
XXXX front+1
XXXXX front+2
... ...
n个数 front+(n-1/2)
*/
int popMiddle() {
int cnt = getCont();
if(cnt == 0){
return -1;
}
int popPos = front + (cnt - 1)/2;
int tmp = data[popPos];
for(int i = popPos; i < rear; ++i){
data[i] = data[i+1];
}
--rear;
return tmp;
}
int popBack() {
if(getCont() == 0){
return -1;
}
int val = data[rear--]; // 弹出Back之后rear变为尾插下个数的前一个位置
return val;
}
};
复杂度分析
时间复杂度:pushFront
、pushBack
、popFront
以及 popBack
一次操作的时间复杂度均为
O
(
1
)
O(1)
O(1),pushMiddle
和 popMiddle
n
次操作的时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。
空间复杂度: O ( n ) O(n) O(n)。
写在最后
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。
如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。
最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。