先看下题目
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出:
[null,null,null,2,1,2]
示例 2:
输入:
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出:
[null,-1,-1]
限制:
1 <= push_back,pop_front,max_value的总操作数 <= 10000
1 <= value <= 10^5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题
1、有总操作数的限制,而不是限制队列长度,可以不做循环队列;
2、value的值是有大小限制的正数,似乎暗示要用哈希表;
3、均摊时间复杂度要求O(1),这有点难。
先来暴力搜索:
#define QUEUE_MAX_CNT 10001
#define QUEUE_MAX_NUM 100000
typedef struct {
int *value;
int max;
int head;
int rear;
} MaxQueue;
MaxQueue* maxQueueCreate() {
MaxQueue *tmp = (MaxQueue *)malloc(sizeof(MaxQueue));
tmp->value = (int *)malloc(sizeof(int)*QUEUE_MAX_CNT);
tmp->max = -1;
tmp->head = tmp->rear = 0;
return tmp;
}
int maxQueueMax_value(MaxQueue* obj) {
return obj->max;
}
void maxQueuePush_back(MaxQueue* obj, int value) {
obj->value[obj->rear++] = value;
if(value > obj->max)
obj->max = value;
}
int maxQueuePop_front(MaxQueue* obj) {
int ret;
if(obj->head < obj->rear){
ret = obj->value[obj->head++];
}else
ret = -1;
//稍微优化一下:队列不为空,且出队的是最大值才重新求队列的最大值
if(obj->head == obj->rear)
obj->max = -1;
else if(ret == obj->max){
int i = 0;
obj->max = obj->value[obj->head];
for(i = obj->head; i < obj->rear; i++){
if(obj->value[i] > obj->max)
obj->max = obj->value[i];
}
}
return ret;
}
void maxQueueFree(MaxQueue* obj) {
free(obj->value);
obj->value = NULL;
free(obj);
obj = NULL;
}
看运行结果,马马虎虎。
思考了一会,无果。决定去参考一下运行最快的程序:
typedef struct {
int que[10000];
int deque[10000];
int queFront, queRear, dequeFront, dequeRear;
} MaxQueue;
MaxQueue* maxQueueCreate() {
MaxQueue *p = (MaxQueue *)malloc(sizeof(MaxQueue));
p->queFront = p->queRear = p->dequeFront = p->dequeRear = 0;
return p;
}
int maxQueueMax_value(MaxQueue* obj) {
if (obj->queFront == obj->queRear)
return -1;
return (obj->deque)[(obj->dequeFront+1)%10000];
}
void maxQueuePush_back(MaxQueue* obj, int value) {
obj->queRear = (obj->queRear + 1) % 10000;
(obj->que)[obj->queRear] = value;
while(obj->dequeFront != obj->dequeRear && (obj->deque)[obj->dequeRear] < value)
obj->dequeRear = (obj->dequeRear - 1 + 10000) % 10000;
obj->dequeRear = (obj->dequeRear + 1) % 10000;
(obj->deque)[obj->dequeRear] = value;
}
int maxQueuePop_front(MaxQueue* obj) {
if (obj->queFront == obj->queRear)
return -1;
obj->queFront = (obj->queFront + 1) % 10000;
int a = (obj->que)[obj->queFront];
if (a == (obj->deque)[(obj->dequeFront+1)%10000])
obj->dequeFront = (obj->dequeFront+1)%10000;
return a;
}
void maxQueueFree(MaxQueue* obj) {
free(obj);
}
/**
* Your MaxQueue struct will be instantiated and called as such:
* MaxQueue* obj = maxQueueCreate();
* int param_1 = maxQueueMax_value(obj);
* maxQueuePush_back(obj, value);
* int param_3 = maxQueuePop_front(obj);
* maxQueueFree(obj);
*/
提交后发现,结果差不多:
也罢、看一下它的思路:
1、创建了两个队列que和deque,一个是常规的队列,另一个暂时还不知道;
2、看一下入队的函数:
void maxQueuePush_back(MaxQueue* obj, int value) {
obj->queRear = (obj->queRear + 1) % 10000;
(obj->que)[obj->queRear] = value;
while(obj->dequeFront != obj->dequeRear && (obj->deque)[obj->dequeRear] < value)
obj->dequeRear = (obj->dequeRear - 1 + 10000) % 10000;
obj->dequeRear = (obj->dequeRear + 1) % 10000;
(obj->deque)[obj->dequeRear] = value;
}
原来deque是一个递减的队列:当入队的数据大于队尾时,队尾的数据就对当前队列的最大值没有任何影响了,所以会被删掉。这种方法叫做滑动窗口,%10000是做了循环队列;
3、出队函数:
int maxQueuePop_front(MaxQueue* obj) {
if (obj->queFront == obj->queRear)
return -1;
obj->queFront = (obj->queFront + 1) % 10000;
int a = (obj->que)[obj->queFront];
if (a == (obj->deque)[(obj->dequeFront+1)%10000])
obj->dequeFront = (obj->dequeFront+1)%10000;
return a;
}
由于使用了滑动,只有que出队的值等于递减队列deque的front值,才对deque进行出队处理。