【每日一题】设计前中后队列

Tag

【设计类】【队列】【数组】【2023-11-28】


题目来源

1670. 设计前中后队列


题目解读

设计一个队列,可以实在在前、中、后三个位置的 push 和 pop 操作。


解题思路

维护一个数组,在数组中使用指针指向前、后两个位置并维护两个的指向。本题还可以使用 vector 容器,可以省去移动的操作,这里没有介绍该方法,读者可以自行实现。

方法一:双指针

初始化

FrontMiddleBackQueue() {
	front = CENTER;
	rear = front - 1;
}

使用两个指针 frontrear 分别指向数组 data 的 “前后” 两个位置。初始化 data 数组,int data[5000],接着初始化 front = 2500rear = front - 1

getCount

int getCont(){
	return rear - front + 1;
}

push 和 pop 操作

前、后两个位置的 pushpop 操作很容易实现:

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) / 2n 为数组此时的长度。

弹出 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;
    }
};

复杂度分析

时间复杂度:pushFrontpushBackpopFront 以及 popBack 一次操作的时间复杂度均为 O ( 1 ) O(1) O(1)pushMiddlepopMiddle n 次操作的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( n ) O(n) O(n)


写在最后

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wang_nn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值