数据结构--队列

Posted 水澹澹兮生烟.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构--队列相关的知识,希望对你有一定的参考价值。

1.队列的基本概念

它只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out)

2.队列的特性

先进先出
入队:进行插入操作的一端称为队尾
出队:进行删除操作的一端称为队头
在这里插入图片描述

3.队列的方法及其实现

在考虑如何实现队列,我们可以使用线性结构进行实现。
1.我们可以考虑使用带头结点的点链表进行实现。
a.初始化

//初始化
void QueueInit(queue* q){
	assert(q);
	q->Qhead = q->Qtail = getnode(0);//头结点的值域不作数
}

b.入队列

//入队列
void Queuepush(queue* q, DType data){
	assert(q);
	//直接在尾部进行插入
	q->Qtail->next = getnode(data);//构造新的结点,直接进行尾插
	q->Qtail = q->Qtail->next;//为指针后移
}

c.出队列
在这里插入图片描述

//出队列
void Queuepop(queue* q){

	assert(q);
	Q_node* delnode = NULL;
	if (Queueempty(q)) return; 
	delnode = q->Qhead->next;
	q->Qhead->next = delnode->next;
	if (delnode->next == NULL)
	{
		//当前队列只有一额元素
		q->Qtail = q->Qhead;
	}
	free(delnode);
}

d.获取队尾和队头元素

//获取队尾和队头元素
DType QueueQhead(queue* q){
	assert(!Queueempty(q));
	return q->Qhead->next->data;
}
DType QueueQtail(queue* q){
	assert(!Queueempty(q));
	return q->Qtail->data;
}

e.获取队列中的元素总个数

//获取队列中的元素总个数
int Queuesize(queue* q){
	int count = 0;
	Q_node* cur = q->Qhead->next;
	while (cur){
		count++;
		cur = cur->next;
	}
	return count;
}

f.检测队列是否为空

int Queueempty(queue* q){
	assert(q); 
	
	return NULL==q->Qhead->next;
}

g.将队列销毁

//将队列销毁
void Queuedestroy(queue* q){
	//将里面的所有节点都进行销毁,将链表从前往后进行头删
	assert(q);
	Q_node* cur = q->Qhead;
	while (cur){
		q->Qhead = cur->next;
		free(cur);
		cur = q->Qhead;
	}
	q->Qhead = q->Qtail = NULL;
}

2.如果我们此时采用顺序表进行实现那我们需要考虑队列里出队列不同的情况。
在这里插入图片描述
这两种情况第二种不会造成假溢出,但是再进行出队的时候,时间复杂度为O(n),需要将后面的元素向前搬移,而采用第一种情况会出现假 溢出。此时我们要使用循环队列去解决假溢出。
假溢出:前面出队后剩余的空间,rear无法再插入。
在这里我们要考虑几个问题:
(1)何判断队列是满是空?
三种方法:
a.少存储一个元素: (rear+1)%N == front;
b.实用计数count: 队列为空,count == 0;队列满,count ==N
c.使用标记: 当入队列,flag=true;出队列,flag=flase.
(2)循环队列长度为N。其队内有效长度为?
(rear-front+N)%N
在这里插入图片描述

typedef struct {//用顺序表实现
int* array;
int head;
int rear;
int count;//有效元素的个数
int capacity;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    //创建一个循环变量
    MyCircularQueue* mq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(NULL==mq){
        return NULL;
    }
    mq->array=(int*) malloc(sizeof(int)*k);//给出array 在内存的空间大小
    if(NULL==mq->array){
        return NULL;
    }
    mq->head=0;
    mq->rear=0;
    mq->count=0;
    mq->capacity=k;
    return mq;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->count==obj->capacity;//当有效数据的个数等于最大可存数据,此时循环队列满了
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->count==0;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(!myCircularQueueIsFull(obj)){
        //队列未满,入队
        obj->array[(obj->rear++)%obj->capacity]=value;
        obj->count++;
        return true;
    }
    return false;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(!myCircularQueueIsEmpty(obj)){
        //进行出队列
        obj->head=(obj->head+1)%obj->capacity;
        obj->count--;
        return true;
    }
    return false;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(obj->count==0) return -1;
    return obj->array[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(obj->count==0) return -1;
    return obj->array[(obj->rear-1+obj->capacity)%obj->capacity];
}




void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->array);
    free(obj);
}

4.用栈实现队列&&用队列实现栈

(1)用队列模拟栈
a.我们首先要知道他们的特性,队列是先进先出,而栈是先入后出;入栈实现容易,但是出栈用一个队列没有办法实现;此时我们用两个队列实现。
用两个队列q1,q2;让其中一个队列起到辅助作用;
入栈:那个队列不空,就将元素放到那个队列中
出栈:先看那个队列中有元素,将该队列中N-1个元素导入到另一个空队列中,最后将该元素删除

或取栈顶元素:那个队列非空直接取队尾元素
在这里插入图片描述

typedef struct {
    queue q1;
    queue q2;
} MyStack;

/** Initialize your data structure here. */

MyStack* myStackCreate() {
 //初始化,栈的结构只可以从堆上申请,最后要进行返回;不能直接创建局部变量mastack ms;,因为最后会销毁
    MyStack* ms=(MyStack*)malloc(sizeof(MyStack));//从堆上申请
    if(NULL==ms){
        return NULL;
    }
    QueueInit(&ms->q1);//初始化q1
    QueueInit(&ms->q2);//初始化q2
    return ms;
}

/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) {
    //入栈;检查哪一个队列不空
    if(!Queueempty(&obj->q2)){
        //直接执行入队操作
        Queuepush(&obj->q2,x);
    }else{
        Queuepush(&obj->q1,x);
    }
}

/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack* obj) {
    int x=0;
    int ret=0;
    //先看那个队列中有元素,将该队列中N-1个元素导入到另一个空队列中,最后将该元素删除
    if(!Queueempty(&obj->q1)){
        while(Queuesize(&obj->q1)>1){
            //先获取队头元素,再在原队列中删除,再插入新队列中
            x=QueueQhead(&obj->q1);
            Queuepush(&obj->q2,x);
            Queuepop(&obj->q1);
        }
        //再将原队列中的最后一个元素进行删除,实现了出栈
        ret=QueueQhead(&obj->q1);
        Queuepop(&obj->q1);
    }else{
         while(Queuesize(&obj->q2)>1){
            //先获取队头元素,再在原队列中删除,再插入新队列中
            x=QueueQhead(&obj->q2);
            Queuepush(&obj->q1,x);
            Queuepop(&obj->q2);
        }
        //再将原队列中的最后一个元素进行删除,实现了出栈
        ret=QueueQhead(&obj->q1);
        Queuepop(&obj->q2);
    }
        
    return ret;
}

/** Get the top element. */
int myStackTop(MyStack* obj) {
    //取栈顶元素,想到于得到队尾元素
    if(!Queueempty(&obj->q1)) {
        return QueueQtail(&obj->q1);
    }
    else {
        return QueueQtail(&obj->q2);
    }
}

/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
    return Queueempty(&obj->q1)&&Queueempty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    //将两个队列先进行销毁,再将obj进行销毁
    Queuedestroy(&obj->q1);
    Queuedestroy(&obj->q2);
    free(obj);
}

(2)用栈模拟实现队列
a.可以使用两个栈模拟队列
b.入队列:直接往s1中插入
出队列:1.检测s2中有无元素
a.有–>直接执行栈操作,相当于出队列
b.无–>将s1中的元素全部搬移到s2中,再执行a步骤
相当于用s1模拟入队列,s2模拟入队列
在这里插入图片描述

typedef struct {
    stack s1;//模拟入栈
    stack s2;//模拟出栈
} MyQueue;

/** Initialize your data structure here. */

MyQueue* myQueueCreate() {
    MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue));
    if(NULL==q){
        return NULL;
    }

    stackInit(&q->s1);
    stackInit(&q->s2);
    return q;
}

/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
    //入队列相当于s1进行入栈
    stackpush(&obj->s1,x);   
}

/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
    int ret=0;
    //出队列,相当于s2进行出栈
    if(!stackempty(&obj->s2)){
        //出栈
        ret=stacktopdata(&obj->s2);
        stackPop(&obj->s2);
    }else{
        while(!stackempty(&obj->s1)){
            //取栈顶元素,保存到s2中,然后出栈
            stackpush(&obj->s2,stacktopdata(&obj->s1));
            stackPop(&obj->s1);
        }
        ret=stacktopdata(&obj->s2);
        stackPop(&obj->s2);
    }
    return ret;
}

/** Get the front element. */
int myQueuePeek(MyQueue* obj) {//获取队头元素
    if(!stackempty(&obj->s2)){
       return stacktopdata(&obj->s2);
    }else{
        while(!stackempty(&obj->s1)){
            //取栈顶元素,保存到s2中,然后出栈
            stackpush(&obj->s2,stacktopdata(&obj->s1));
            stackPop(&obj->s1);
        }
       return stacktopdata(&obj->s2);
    }
}

/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
    return stackempty(&obj->s1)&&stackempty(&obj->s2);
}

void myQueueFree(MyQueue* obj) {
    stackDestroy(&obj->s1);
    stackDestroy(&obj->s2);
    free(obj);
}

以上是关于数据结构--队列的主要内容,如果未能解决你的问题,请参考以下文章

perl中的队列

IPC System V 消息队列 - 发送一个数组块

JDK常用数据结构

完全下载文件时,将下载的文件从一个片段传递到另一个片段

RocketMQ - 如何用死信队列解决消费者异常

rabbitmq - 不会获取队列中的所有消息