Leetcode循环队列(数组实现及链表实现)

简介: Leetcode循环队列(数组实现及链表实现)

队列的介绍

 队列是一种只允许在一段进行插入,在另一端进行删除的数据操作的特殊线性结构,,因此决定了他具有先入先出的特点,其中进行插入操作的一段叫做队尾,出队列的一端叫做队头。

队列的实现

 队列可以使用链表或者数组进行实现,对于这两种实现方法,使用链表实现效果更好一点,两个指针中front为链表的头,即队列的队头,出数据的话只需要找到front的下一个假设为pre,将front销毁,front置为pre即可,如果是用数组的结构的话,出队列在数组头上出数据,效率会很低。

链表实现队列代码如下

Queue.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
  struct QueueNode* next;
  QDataType data;
}QNode;
typedef struct Queue
{
  QNode* head;//头
  QNode* tail;//尾
  int size;//大小
}Que;
void QueueInit(Que* pq);//初始化
void QueuePush(Que* pq,QDataType);//入队列
void QueueDestroy(Que* pq);//销毁
void QueuePop(Que* pq);//出队列
QDataType QueueFront(Que* pq);//队头的数据
QDataType QueueBack(Que* pq);//队尾的数据
bool QueueEmpty(Que* pq);//检查是否为空
int QueueSize(Que* pq);//队列元素

Queue.c

#define _CRT_SECURE_NO_WARNINGS
#include "Queueh.h"
void QueueInit(Que* pq)
{
  assert(pq);
  pq->head = pq->tail = NULL;
  pq->size = 0;
}
void QueueDestroy(Que* pq)
{
  assert(pq);
  QNode* cur = pq->head;
  while (cur != NULL)
  {
    QNode* next = cur->next;
    free(cur);
    cur = next;
  }
  pq->head = pq->tail = NULL;
  pq->size = 0;
}
void QueuePush(Que* pq, QDataType x)
{
  assert(pq);
  QNode* newnode = (QNode*)malloc(sizeof(QNode));
  newnode->next = NULL;
  if (newnode == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  newnode->data = x;
  if (pq->tail == NULL)//这里要先进行判断
  {
    pq->head = pq->tail = newnode;//如果队列里没有一个元素
    //那么head和tail都为空,将newnode设置为队头,当然也是队尾,毕竟只有一个元素。
  }
  else
  {
    pq->tail->next = newnode;
    pq->tail = newnode;
  }
  pq->size++;
}
void QueuePop(Que* pq)
{
  assert(pq);
  assert(!QueueEmpty(pq));//判空,避免引起不必要的错误。
  if (pq->head->next != NULL)
  {
    QNode* next = pq->head->next;
    free(pq->head);
    pq->head = next;
  }
  else
  {
    free(pq->head);
    pq->head = pq->tail = NULL;
  }
  pq->size--;
}
QDataType QueueFront(Que* pq)
{
  assert(pq);
  //判断是否为空
  assert(!QueueEmpty(pq));
  return pq->head->data;//队头位置
}
QDataType QueueBack(Que* pq)
{
  assert(pq);
  //判断是否为空
  assert(!QueueEmpty(pq));
  return pq->tail->data;//队尾位置
}
bool QueueEmpty(Que* pq)//判断是否为空,为空则返回true
{
  if (pq->head == NULL)
  {
    return true;
  }
  else
  {
    return false;
  }
}
int QueueSize(Que* pq)
{
  assert(pq);
  return pq->size;
}

进入正题

链接:设计循环队列

 前边的队列如果空间不够就会扩容,但是这里的循环队列大小是固定的,只可以保存k个元素,当然还是遵循先入先出的规则

 例如下边的环形队列,在pop掉队头数据后,这块空间不会被销毁的,可以继续存储值覆盖原来的值,假设k等于5,当入6个元素后,这个循环队列就满了,当出队列时,此时这个队列的首位置就可以继续存储数据。

数组的方法

结构体声明如下

typedef struct {
    int* a;
    int front;
    int rear;//尾
    int k;
} MyCircularQueue;

初始化函数如下

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个方便区分空和满
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->k=k;
    obj->front=obj->rear=0;
    return obj;
}

数组也面临一个同样的问题,如何判断是满还是空?

可以多开一块空间。k==5,开6块空间,最后一块空间浪费不用。

如果front等于tail就为空,在这里设置tail的值为0~5(要注意下标和位置有减一的关系),如果tail的下一个位置为front时,表示队列已满。

obj->tail%=(obj->k+1);//obj为循环队列变量

控制tail的值的变化范围,当tail等于6时置tail为0。

当然,在出队列过程中,front是会不断变化的。

我们看front变化的情况

pop一个数据后,向后移一位

判断是否队列已满的条件判断句如下

(obj->tail+1)%(obj->k+1 )==obj->front

前边已经说过了,tail的变化范围为0~5,此时tail被置为0,但front为1,不相等,就表示还有空余的位置,队列没有满,所以上边的判断语句在任何场景都是正常使用的。

判断是否已满函数如下(题目中tail被rear替换)

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->k+1  )==obj->front;
}

返回类型为布尔值,如果相等就返回true,不相等就返回false。

判断是否为空很简单,直接比较rear和front的值是否相等即可。

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front==obj->rear;
}

置空函数

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

获取队头元素

很简单,返回front位置的元素即可

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->a[obj->front];
    }
}

获取队尾元素

这里就要思考一下了

如果rear不在数组第一个空间上,直接返回数组rear-1处的值即可,当rear位于数组首元素,就要返回数组第k个元素。

代码如下

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        if(obj->rear==0)
        {
            return obj->a[obj->k];
        }
        else{
            return obj->a[obj->rear-1];
        }
    //下边是综合写法
        //return obj->a[(obj->rear+obj->k)%(obj->k+1)];
    }
}

出队列deQueue()

出队列只需要将front++即可,就算之前的数据不销毁,下次入队列操作也会覆盖他的数据。

代码如下

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    ++obj->front;
    obj->front%=(obj->k+1);
    return true;
}

注意是要有返回值的,如果队列为空,就没有出数据,返回false,出数据成功就返回true。

入队列

代码如下:

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->a[obj->rear]=value;
    obj->rear++;
    obj->rear%=(obj->k+1);
    return true;
}

 首先判断循环队列是否已满,如果已满就返回false,如果没有满就将rear位置的值改为value,然后rear++,判断是否超出范围,如果超出就置为0。最后入队列成功返回true。

 到了这里这道题目就顺利解决了,如果使用双向链表来做这道题的话当然也可以,但是会稍微麻烦一点,有兴趣可以尝试尝试。

今天的内容就到这里啦,如果有错误欢迎各位指正哦!

综合上述代码后即可通过本题。

链表实现

链表实现就较为简单了,思路和前边数组实现的思路很像。

首先声明结构体

typedef struct listNode
{
    struct listNode* next;
    int val;
}LS;
typedef struct {
    LS* head;
    LS* tail;
    int size;
    int k;
} MyCircularQueue;

节点的结构体保存下一个结构体的指针,并且存储值val。

循环链表结构体有头结点,尾节点,size是队列中元素的个数,k为设定的循环队列的容量。

入队列时,获取一个初始化后新节点,并把之前的尾结点与这个节点连接起来。

获取新节点的函数如下

LS* buylistNode(int x)
{
    LS*listnode =(LS*)malloc(sizeof(LS));
    listnode ->next=NULL;
    listnode ->val=x;
    return listnode;
}

函数返回新造的节点。以供入队列函数使用。


至于判空判满函数我想就不用过多介绍,重要的是入队列和出队列。

出队列只需要头结点向后移,然后size–就好。

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(obj->head==NULL)
    {
        return false;
    }
    else
    {
        obj->head=obj->head->next;
        obj->size--;
        return true;
    }
}

入队列

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))//如果已满,就返回false
    {
        return false;
    }
    LS*node=buylistNode(value);
    if(obj->head==NULL)//第一次插入
    {
        obj->head=node;
        obj->tail=node;
    }
    else
    {
        LS*tran=obj->tail;
        obj->tail=node;
        tran->next=obj->tail;
    }
    obj->size++;//注意size++
    return true;
}

全部代码如下

typedef struct listNode
{
    struct listNode* next;
    int val;
}LS;
typedef struct {
    LS* head;
    LS* tail;
    int size;
    int k;
} MyCircularQueue;
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->size==0;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->size==obj->k;
}
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->head=NULL;
    obj->tail=NULL;
    obj->size=0;
    obj->k=k;
    return obj;
}
LS* buylistNode(int x)
{
    LS*listnode =(LS*)malloc(sizeof(LS));
    listnode ->next=NULL;
    listnode ->val=x;
    return listnode;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    LS*node=buylistNode(value);
    if(obj->head==NULL)
    {
        obj->head=node;
        obj->tail=node;
    }
    else
    {
        LS*tran=obj->tail;
        obj->tail=node;
        tran->next=obj->tail;
    }
    obj->size++;
    return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(obj->head==NULL)
    {
        return false;
    }
    else
    {
        obj->head=obj->head->next;
        obj->size--;
        return true;
    }
}
int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->head->val;
}
int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->tail->val;
}
void myCircularQueueFree(MyCircularQueue* obj) {
    while(obj->head!=NULL)
    {
        LS*a=obj->head->next;
        free(obj->head);
        obj->head=a;
    }
    free(obj);
}

这篇文章到这里就结束啦,如果有问题欢迎大家指出。

目录
相关文章
|
15天前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
30 1
|
22天前
Leetcode第21题(合并两个有序链表)
这篇文章介绍了如何使用非递归和递归方法解决LeetCode第21题,即合并两个有序链表的问题。
43 0
Leetcode第21题(合并两个有序链表)
|
27天前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
37 0
|
27天前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
84 64
|
22天前
【LeetCode-每日一题】 删除排序数组中的重复项
【LeetCode-每日一题】 删除排序数组中的重复项
17 4
|
22天前
|
索引
Leetcode第三十三题(搜索旋转排序数组)
这篇文章介绍了解决LeetCode第33题“搜索旋转排序数组”的方法,该问题要求在旋转过的升序数组中找到给定目标值的索引,如果存在则返回索引,否则返回-1,文章提供了一个时间复杂度为O(logn)的二分搜索算法实现。
13 0
Leetcode第三十三题(搜索旋转排序数组)
|
22天前
LeetCode第二十四题(两两交换链表中的节点)
这篇文章介绍了LeetCode第24题的解法,即如何通过使用三个指针(preNode, curNode, curNextNode)来两两交换链表中的节点,并提供了详细的代码实现。
13 0
LeetCode第二十四题(两两交换链表中的节点)
|
22天前
Leetcode第十九题(删除链表的倒数第N个节点)
LeetCode第19题要求删除链表的倒数第N个节点,可以通过快慢指针法在一次遍历中实现。
36 0
Leetcode第十九题(删除链表的倒数第N个节点)
|
22天前
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
54 0
|
22天前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
16 0