数据结构——队列

2.队列

2.1队列的概念及结构

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

入队列:进行插入操作的一端称为队尾 

出队列:进行删除操作的一端称为队头

2.2队列的实现

还是那个问题,用数组结构还是链式结构来实现尾删呢?

数组结构已经明显不适合了,每次出队列都要遍历一遍,而链式结构的则没有这个问题。而且单链表就可以。

如果你还记得,单链表尾插的结构也是O(N)的时间复杂度,为什么可以用链式结构?

原因在于:单链表不止存在尾插,还有尾删,需要尾结点前一个节点的位置,使用尾指针没有太大意义,而队列中的链式结构只存在一个尾插,用尾指针可以很好的将时间复杂度优化为O(1)。

大体逻辑和单链表一样,如果无法理解,不是队列的问题,而是之前单链表没有学好。

不同的地方在于,队列要有两个结构体,一个链式队列节点结构体,一个链式队列首节点尾节点结构体,其他的就是考虑一些特殊情况,有漏写的也能通过测试测出来。

下面给出队列的代码:

Queue.c中

#include"Queue.h"

void QueueInit(Queue* pq)

{

        assert(pq);

        pq->head = pq->tail = NULL;

}

void QueueDestroy(Queue* pq)

{

        assert(pq);

        QueueNode* cur = pq->head;

        while (cur)

        {

               QueueNode* next = cur->next;

               free(cur);

               cur = next;

        }

        pq->head = pq->tail = NULL;

}

void QueuePush(Queue* pq, QueueDateType x)

{

        assert(pq);

        QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));

        newNode->val = x;

        newNode->next = NULL;

        if (pq->head == NULL)

        {

               pq->head = pq->tail = newNode;

        }

        else

        {

               pq->tail->next = newNode;

               pq->tail = newNode;

        }

}

void QueuePop(Queue* pq)

{

        assert(pq);

        if (pq->head == NULL)

               return;

        if (pq->head->next == NULL)

        {

               free(pq->head);

               pq->head = pq->tail = NULL;

        }

        else

        {

               QueueNode* next = pq->head->next;

               free(pq->head);

               pq->head = next;

        }

        

}

bool QueueEmpty(Queue* pq)

{

        if (pq->head == NULL)

               return true;

        else

               return false;

}

size_t QueueSize(Queue* pq)//如果需要频繁查看队列的元素个数,可以在QueueNode结构体中额外定义一个size变量。

{

        assert(pq);

        size_t size = 0;

        QueueNode* cur = pq->head;

        while (cur)

        {

               size++;

               cur = cur->next;

        }

        return size;

}

QueueDateType QueueFront(Queue* pq)

{

        assert(pq);

        assert(pq->head);

        return pq->head->val;

}

QueueDateType QueueBack(Queue* pq)

{

        assert(pq);

        assert(pq->tail);

        return pq->tail->val;

}

Queue.h中

#pragma once

#include<stdio.h>

#include<stdlib.h>

#include<Stdbool.h>

#include<assert.h>

typedef int QueueDateType;

typedef struct QueueNode

{

        QueueDateType val;

        struct QueueNode* next;

}QueueNode;

typedef struct Queue

{

        QueueNode* head;

        QueueNode* tail;

}Queue;

void QueueInit(Queue* pq);

void QueueDestroy(Queue* pq);

void QueuePush(Queue* pq, QueueDateType x);

void QueuePop(Queue* pq);

bool QueueEmpty(Queue* pq);

size_t QueueSize(Queue* pq);

QueueDateType QueueFront(Queue* pq);

QueueDateType QueueBack(Queue* pq);

test.c中

对队列进行测试

然后就是队列的OJ题了

1.用队列实现栈

基本思路:

入栈,push数据到不为空的队列中

出栈,将不为空队列的前n-1个数据放入到另一个队列中,剩下的pop。

逻辑很简单,复杂的是结构

顺便说一下,这个代码逻辑简单,结构复杂,最好自己写,出问题了再对照代码看是什么问题。

还是无法理解就复制到编译器中,然后根据测试用例来调试。

还有一种方法,在OJ题上来进行排除法锁定出错的函数。对测试用例进行删除。下图1为一个出错的测试用例,图2位排除法后的测试用例,直到出现符合期望的情况。在删除pop和empty后可以正常运行,说明,问题在pop上。当然,这是大部分情况。

代码如下:

//标注行以上为队列的实现

typedef int QueueDateType;

typedef struct QueueNode

{

        QueueDateType val;

        struct QueueNode* next;

}QueueNode;

typedef struct Queue

{

        QueueNode* head;

        QueueNode* tail;

}Queue;

void QueueInit(Queue* pq)

{

        assert(pq);

        pq->head = pq->tail = NULL;

}

void QueueDestroy(Queue* pq)

{

        assert(pq);

        QueueNode* cur = pq->head;

        while (cur)

        {

               QueueNode* next = cur->next;

               free(cur);

               cur = next;

        }

        pq->head = pq->tail = NULL;

}

void QueuePush(Queue* pq, QueueDateType x)

{

        assert(pq);

        QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));

        newNode->val = x;

        newNode->next = NULL;

        if (pq->head == NULL)

        {

               pq->head = pq->tail = newNode;

        }

        else

        {

               pq->tail->next = newNode;

               pq->tail = newNode;

        }

}

void QueuePop(Queue* pq)

{

        assert(pq);

        if (pq->head == NULL)

               return;

        if (pq->head->next == NULL)

        {

               free(pq->head);

               pq->head = pq->tail = NULL;

        }

        else

        {

               QueueNode* next = pq->head->next;

               free(pq->head);

               pq->head = next;

        }     

}

bool QueueEmpty(Queue* pq)

{

        if (pq->head == NULL)

               return true;

        else

               return false;

}

size_t QueueSize(Queue* pq)

{

        assert(pq);

        size_t size = 0;

        QueueNode* cur = pq->head;

        while (cur)

        {

               size++;

               cur = cur->next;

        }

        return size;

}

QueueDateType QueueFront(Queue* pq)

{

        assert(pq);

        assert(pq->head);

        return pq->head->val;

}

QueueDateType QueueBack(Queue* pq)

{

        assert(pq);

        assert(pq->tail);

        return pq->tail->val;

}

//标注行

typedef struct 

{

    Queue q1;

    Queue q2;

} MyStack;

MyStack* myStackCreate()

{

    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));//这里不能定义局部变量,出作用域会销毁。

    QueueInit(&(obj->q1));

    QueueInit(&(obj->q2));

    return obj;

}

void myStackPush(MyStack* obj, int x) 

{

    if(QueueEmpty(&obj->q2))

    {

        QueuePush(&obj->q1, x);

    }

    else

    {

        QueuePush(&obj->q2, x);

    }

}

int myStackPop(MyStack* obj) 

{

    if(QueueEmpty(&obj->q2))

    {

        while((&obj->q1)->head->next)

        {

            QueuePush(&obj->q2,(&obj->q1)->head->val);

            QueuePop(&obj->q1);

        }

        int tmp = (&obj->q1)->head->val;

        QueuePop(&obj->q1);

        return tmp;

    }

    else

    {

        while((&obj->q2)->head->next)

        {

            QueuePush(&obj->q1,(&obj->q2)->head->val);

            QueuePop(&obj->q2);

        }

        int tmp = (&obj->q2)->head->val;

        QueuePop(&obj->q2);

        return tmp;

    }

}

int myStackTop(MyStack* obj)

{

    if(QueueEmpty(&obj->q2))

    {

        int tmp = QueueBack(&obj->q1);

        return tmp;

    }

    else

    {

        int tmp = QueueBack(&obj->q2);

        return tmp;

    }

}

bool myStackEmpty(MyStack* obj)

{

    if(QueueEmpty(&obj->q2) && QueueEmpty(&obj->q1))

    {

        return true;

    }

    else

    {

        return false;

    }

}

void myStackFree(MyStack* obj)

{

    if(QueueEmpty(&obj->q2) && QueueEmpty(&obj->q1))

    {

        free(obj);

        return ;

    }

    else if(QueueEmpty(&obj->q2))

    {

        free(obj);

        QueueDestroy(&obj->q1);

    }

    else

    {

        free(obj);

        QueueDestroy(&obj->q2);

    }

}

2.用栈实现队列

本题思路:有两个栈,一个存储数据1,2,3,4,5,现在要实现队列,意味着要按1,2,3,4,5出,所以先将栈的所有值导出,另一个栈中存储5,4,3,2,1。正好符合栈出的顺序。所以需要出就是按另一个栈的顺序出,出完了就再次从第一个队列导入,所以可以将两个栈取名,一个入队列,一个出队列。

代码如下:

//以下到标注行是栈的实现

typedef int StackDateType;

typedef struct Stack

{

    StackDateType* a;

    int top;

    int capacity;

}Stack;

void StackInit(Stack* ps)

{

    assert(ps);

    ps->a = NULL;

    ps->top = 0;

    ps->capacity = 0;

}

void StackDestroy(Stack* ps)

{

    assert(ps);

    free(ps->a);

    ps->a = NULL;

    ps->capacity = 0;

    ps->top = 0;

}

void StackPush(Stack* ps, StackDateType x)

{

    assert(ps);

    if (ps->top == ps->capacity)

    {

        int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

        StackDateType* tmp = (StackDateType*)realloc(ps->a, newCapacity * sizeof(StackDateType));

        if (tmp == NULL)

        {

            printf("realloc fail\n");

            return;

        }

        else

        {

            ps->a = tmp;

            ps->capacity = newCapacity;

        }

    }

    ps->a[ps->top] = x;

    ps->top++;

}

void StackPop(Stack* ps)

{

    assert(ps);

    if (ps->top > 0)

        ps->top--;

}

StackDateType StackTop(Stack* ps)

{

    assert(ps);

    assert(ps->top > 0);

    return ps->a[ps->top-1];

}

bool StackEmpty(Stack* ps)

{

    assert(ps);

    if (ps->top > 0)

        return false;

    else

        return true;

}

int StackSize(Stack* ps)

{

    assert(ps);

    return ps->top;

}

//标注行

typedef struct 

{

    Stack in;

    Stack out;

} MyQueue;

MyQueue* myQueueCreate() 

{

    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));

    StackInit(&obj->in);

    StackInit(&obj->out);

    return obj;

}

void myQueuePush(MyQueue* obj, int x) {

    StackPush(&obj->in,x);

}

int myQueuePop(MyQueue* obj) 

{

    if(StackEmpty(&obj->out))

    {

        while(!StackEmpty(&obj->in))

        {

            StackPush(&obj->out,StackTop(&obj->in));

            StackPop(&obj->in);

        }

        int tmp = StackTop(&obj->out);

        StackPop(&obj->out);

        return tmp;

    }

    else

    {

        int tmp = StackTop(&obj->out);

        StackPop(&obj->out);

        return tmp;

    }

}

int myQueuePeek(MyQueue* obj) 

{

    if(StackEmpty(&obj->out))

    {

        while(!StackEmpty(&obj->in))

        {

            StackPush(&obj->out,StackTop(&obj->in));

            StackPop(&obj->in);

        }

        return StackTop(&obj->out);

    }

    else

    {

        return StackTop(&obj->out);

    }

}

bool myQueueEmpty(MyQueue* obj) 

{

    return StackEmpty(&obj->in) && StackEmpty(&obj->out);

}

void myQueueFree(MyQueue* obj) 

{

    if(StackEmpty(&obj->in) && StackEmpty(&obj->out))

    {

        free(obj);

        return;

    }

    else if(StackEmpty(&obj->in))

    {

        StackDestroy(&obj->out);

        free(obj);

        return ;

    }

    else if(StackEmpty(&obj->out))

    {

        StackDestroy(&obj->in);

        free(obj);

        return ;

    }

    else

    {

        StackDestroy(&obj->in);

        StackDestroy(&obj->out);

        free(obj);

        return ;

    }

}

3.设计循环队列(有点难度)

循环队列就是要形成一个环状的队列,逻辑上呈现的如下图所示,环状队列不增容,满足在任意时间点储存数据的量不超过队列容量的情况。

可以通过数组实现,也可以通过链表实现,数组实现就是在越过下标极限后队列尾置为0。队列尾始终指向将要输入数据的空间。链表更符合环形队列的逻辑,即使到最后一个节点也只需要tail = tail->next就可以了。但以优劣性来讲,数组实现比链表更优,因为数组是连续的空间,而链表唯一比数组优的就是队尾不需要额外的判断。链表的删除是不能free的,因为空间大小是固定的,头节点直接指向下一个就可以了。而且链表取最后一个数据不好取。(队列是可以知道尾节点数据的)

循环队列的缺陷:容量不是缺陷,循环队列就是解决容量有限的情况。缺陷在于如何判断队列满和队列空的情况。一开始是空,此时front和tail指向同一空间,所以front == tail时为空

那么这种情况呢?

tail在完成最后一个空间的赋值后又和front指向同一空间。为了解决这种情况,往往会多开辟一个空间,有4个数据要储存,就开辟5个空间。当tail+1 == front时即为空间满。空的空间位置不固定。

本题最好画图写。

代码如下://涉及if的,都是特殊情况

typedef struct 

{

    int* a;

    int front;

    int tail;

    int k;//队列长度大小

} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);//提前声明函数,因为判断空满的函数在需要使用空满函数的下面。

bool myCircularQueueIsFull(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) 

{

    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));

    obj->a = (int*)malloc(sizeof(int)*(k+1));//多开辟一个节点

    obj->k = k;

    obj->front = obj->tail = 0;

    return obj;

}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 

{

    if(myCircularQueueIsFull(obj))

    {

        return false;

    }

    else

    {

        obj->a[obj->tail] = value;

        if(obj->tail == obj->k)//也可以通过tail++后取k+1的模来控制位置

        {

            obj->tail = 0;

        }

        else

        {

            obj->tail++;

        }

        return true;

    }

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 

{

    if(myCircularQueueIsEmpty(obj))

    {

        return false;

    }

    else

    {

        if(obj->front == obj->k)

        {

            obj->front = 0;

        }

        else

        {

            obj->front++;

        }

        return true;

    }

}

int myCircularQueueFront(MyCircularQueue* obj) 

{

    if(myCircularQueueIsEmpty(obj))

    {

        return -1;

    }

    else

    {

        return obj->a[obj->front];

    }

}

int myCircularQueueRear(MyCircularQueue* obj) 

{

    if(myCircularQueueIsEmpty(obj))

    {

        return -1;

    }

    else

    {

        if(obj->tail == 0)

        {

            return obj->a[obj->k];

        }

        else

        {

            return obj->a[obj->tail-1];

        }

    }

}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 

{

    return obj->front == obj->tail;

}

bool myCircularQueueIsFull(MyCircularQueue* obj) 

{

    if(obj->tail == obj->k && obj->front == 0)

    {

        return true;

    }

    else

    {

        return obj->tail+1 == obj->front;

    }

}

void myCircularQueueFree(MyCircularQueue* obj) 

{

    free(obj->a);

    free(obj);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值