剑指Offer-数据结构 59 - II. 队列的最大值
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
限制:
1 <= push_back,pop_front,max_value的总操作数 <= 10000
1 <= value <= 10^5
这个题目其实就是窗口最大值的双向单调递减队列的实现。
使用一个变量记录最大值,在元素入队时记录即可,但当最大值出队后就无法确定下一个最大值了,所以可以用一个单调递减的队列记录下来,每次取最大值即取出队列头部元素即可。
1、当元素入队push_back()时,需要从尾部弹出所有比该元素小的值。
2、当pop_front()时,若出队的元素是最大元素,则双向队列单调队列需要同时将首元素出队。
要实现这个功能,需要先实现基本的队列功能和双向队列功能,然后再实现这个最大优先队列的逻辑。
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "uthash.h"
typedef short int int16_t;
typedef int int32_t;
typedef int bool;
#define TRUE 1
#define FALSE 0
#define NUL '\0'
/*************************************************
* 使用单链表实现基本的队列操作
*************************************************/
typedef struct LINKNODE {
int val;
struct LINKNODE *next;
}LinkNode;
typedef struct {
LinkNode *front;
LinkNode *rear;
}Queue;
Queue *queueCreat() {
//头部节点用于记录队列节点数量
Queue *obj = (Queue*)malloc(sizeof(Queue));
LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
newNode->val = 0;
obj->front = newNode;
obj->rear = newNode;
return obj;
}
void queuePush(Queue* obj, int x) {
LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
newNode->val = x;
obj->rear->next = newNode;
obj->rear = newNode;
//队列节点数+1
obj->front->val += 1;
}
void queuePop(Queue* obj) {
if (queueEmpty(obj)) {
return;
}
//跳过表头取第一个实际的数据
LinkNode *removedNode = obj->front->next;
obj->front->next = removedNode->next;
//如果队列只有表头后1个节点,把唯一的数据节点移除后,需要修改尾部的rear指向。
if(removedNode == obj->rear) {
obj->rear = obj->front;
}
//释放队列头后的第一个节点
free(removedNode);
//队列节点数-1
obj->front->val -= 1;
}
bool queueEmpty(Queue *obj) {
return obj->front->val == 0;
}
//取头节点数据
int queueFront(Queue *obj) {
//跳过表头取第一个数据节点
return obj->front->next->val;
}
//取尾节点数据
int queueBack(Queue *obj) {
return obj->rear->val;
}
//释放队列
void queueFree(Queue *obj) {
while (obj->front != obj->rear) {
queuePop(obj);
}
}
void queueDisplay(Queue *obj) {
printf("queue: %d\n", obj->front->val);
LinkNode *p = obj->front->next;
while(p) {
printf(" %d ", p->val);
p = p->next;
}
}
/*************************************************
* 双向队列
*************************************************/
typedef struct DOUBLE_LINK_LIST {
int val;
struct DOUBLE_LINK_LIST *prev, *next;
}DoubleLinkList;
typedef struct {
DoubleLinkList *head;
DoubleLinkList *tail;
}Deque;
Deque *dequeCreate() {
Deque *obj = (Deque*)malloc(sizeof(Deque));
obj->head = (DoubleLinkList *)malloc(sizeof(DoubleLinkList));
obj->tail = (DoubleLinkList *)malloc(sizeof(DoubleLinkList));
obj->head->next = obj->tail;
obj->tail->prev = obj->head;
obj->head->val = 0;
return obj;
}
bool dequeEmpty(Deque *obj) {
return obj->head->val == 0;
}
//从队头出队还是队尾出队由isTail决定
void dequePop(Deque *obj, int isTail) {
if (dequeEmpty(obj)) {
return;
}
DoubleLinkList *removedNode = isTail ? obj->tail->prev : obj->head->next;
removedNode->next->prev = removedNode->prev;
removedNode->prev->next = removedNode->next;
printf("\n<--deque pop: %d from %d\n", removedNode->val, isTail);
free(removedNode);
obj->head->val -= 1;
}
//从队头入队还是队尾入队由isTail决定
void dequePush(Deque *obj, int value, int isTail) {
//添加在p的左侧
DoubleLinkList *p = isTail ? obj->tail : obj->head->next;
printf("\n--->deque number: %d\n", obj->head->val);
printf("\n--->deque push: %d from %d\n", value, isTail);
DoubleLinkList *newNode = (DoubleLinkList *)malloc(sizeof(DoubleLinkList));
newNode->val = value;
//插入新节点
newNode->prev = p->prev;
p->prev->next = newNode;
newNode->next = p;
p->prev = newNode;
obj->head->val += 1;
}
int dequeFront(Deque *obj) {
return obj->head->next->val;
}
int dequeBack(Deque *obj) {
return obj->tail->prev->val;
}
void dequeFree(Deque *obj) {
while(!dequeEmpty(obj)) {
dequePop(obj, 1);
}
}
void dequeDisplay(Deque *obj) {
printf("\ndeque: %d\n", obj->head->val);
DoubleLinkList *p = obj->head->next;
while(p != obj->tail) {
printf(" %d ", p->val);
p = p->next;
}
}
/*************************************************
* 最大优先队列
*************************************************/
typedef struct MAX_QUEUE {
Queue *queue;//普通队列
Deque *deque;//单调递减双向队列
}MaxQueue;
MaxQueue *maxQueueCreate() {
MaxQueue *obj = (MaxQueue *)malloc(sizeof(MaxQueue));
obj->queue = queueCreat();
obj->deque = dequeCreate();
return obj;
}
int maxQueueMaxValue(MaxQueue *obj) {
if(dequeEmpty(obj->deque)){
return -1;
}
return dequeFront(obj->deque);
}
void maxQueuePushBack(MaxQueue *obj, int value) {
//入普通队列
queuePush(obj->queue, value);
//将比value小的全部从尾部出队列
while(!dequeEmpty(obj->deque) && dequeBack(obj->deque) < value) {
dequePop(obj->deque, 1);
}
//从尾部入队列
dequePush(obj->deque, value, 1);
}
int maxQueuePopFront(MaxQueue *obj) {
if(queueEmpty(obj->queue)) return -1;
int ret = queueFront(obj->queue);
queuePop(obj->queue);
//如果从普通队列出队的元素刚好是最大优先队列的首部元素,
//则最大优先队列的首部元素也需要出队列。
if(ret == dequeFront(obj->deque)) {
dequePop(obj->deque, 0);
}
return ret;
}
void maxQueueFree(MaxQueue *obj) {
queueFree(obj->queue);
dequeFree(obj->deque);
free(obj);
}
/*************************************************
* Demo
*************************************************/
int main() {
MaxQueue *maxQueue = maxQueueCreate();
maxQueuePushBack(maxQueue, 1);
maxQueuePushBack(maxQueue, 3);
maxQueuePushBack(maxQueue, -1);
maxQueuePushBack(maxQueue, -3);
maxQueuePushBack(maxQueue, 5);
maxQueuePushBack(maxQueue, 3);
maxQueuePushBack(maxQueue, 6);
maxQueuePushBack(maxQueue, 7);
queueDisplay(maxQueue->queue);
dequeDisplay(maxQueue->deque);
return 0;
}
小结:
最大优先队列考题中出现的概率还是比较大,是队列这种数据机构的比较好的一个应用。可以利用双向链表实现最大优先队列,实现的代码其实比较简单。