leetCode 622设计循环链表(C语言版)

题目

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。

思路设计

队列有顺序存储结构和链式存储结构两种选择,循环队列就是使用数组这种顺序存储结构来实现的,链队列就是使用链表这种链式存储结构来实现的。本题要求设计循环链表,那就需要使用顺序存储结构来实现。
我们需要一个头标记、一个尾标记来记录队列头和尾,还要一个参数记录顺序存储内存长度,一个数据类型指针。
我设计的循环链表当尾标记和头标记相等时为空,尾标记大于头标记 1 时队列为满。那么判断为满的数学表达式就是:(尾标记 + 1) % queueSize == 头标记
为方便叙述,循环队列里的头尾标记用头尾指针代替

上代码

 #include 
typedef int QElemType;

typedef struct
{
    QElemType *pBase;  // 链表起点
    int front; // 头标记
    int rear; //尾标记
    int maxSize; // 队列长度
} MyCircularQueue;

/** Initialize your data structure here. Set the size of the queue to be k. */
MyCircularQueue *myCircularQueueCreate(int k)
{
    MyCircularQueue *circularQueue = malloc(sizeof(MyCircularQueue)); 
    if (!circularQueue)
    {
        return NULL;
    }
    circularQueue->pBase = (QElemType *)malloc(sizeof(QElemType) * (k+1)); // 尾指针所指内存不存数据,所以队列实际长度比K大1
    if (!circularQueue->pBase)
    {
        return NULL;
    }
    circularQueue->maxSize = k+1;
    circularQueue->front = 0; // 头尾指针指向队列第一个位置,此时为空
    circularQueue->rear = 0;
    return circularQueue;
}

/** Insert an element into the circular queue. Return true if the operation is successful. */
bool myCircularQueueEnQueue(MyCircularQueue *obj, int value)
{
    if ((obj->rear + 1) % obj->maxSize == obj->front) // 判断队列是否满
    {
        return false;
    }
    obj->pBase[obj->rear] = value; // 给尾指针所指内存赋值
    obj->rear = (obj->rear + 1) % obj->maxSize; // 更新尾指针
    return true;
}

/** Delete an element from the circular queue. Return true if the operation is successful. */
bool myCircularQueueDeQueue(MyCircularQueue *obj)
{
    if ((obj->rear == obj->front)) // 判单队列是否为空
    {
        return false;
    }
    obj->front = (obj->front + 1) % obj->maxSize; // 考虑头指针在内存尾部,再次出队后头指针移动到内存头部的情况
    return true;
}

/** Get the front item from the queue. */
int myCircularQueueFront(MyCircularQueue *obj)
{
    if ((obj->rear == obj->front))
    {
        return -1;
    }
    int frontItem = obj->pBase[obj->front];
    return frontItem;
}

/** Get the last item from the queue. */
int myCircularQueueRear(MyCircularQueue *obj)
{
    if ((obj->rear == obj->front))
    {
        return -1;
    }
     if(0==obj->rear&&obj->front>obj->rear){ // 这里要判断是因为尾指针不存数据,直接用数组下标去最后一个数据就会出现
        return obj->pBase[obj->maxSize-1]; //obj->pBase[obj->rear - 1] 下标减一的情况,意味着rear必须大于0,不然数组越界
    }
    if (obj->rear > 0)
    {
        int tailItem = obj->pBase[obj->rear - 1];
        return tailItem;
    }
     return -1;
}

/** Checks whether the circular queue is empty or not. */
bool myCircularQueueIsEmpty(MyCircularQueue *obj)
{
    if ((obj->rear == obj->front))
    {
        return true;
    }
    else
    {
        return false;
    }
}

/** Checks whether the circular queue is full or not. */
bool myCircularQueueIsFull(MyCircularQueue *obj)
{
    if ((obj->rear + 1) % obj->maxSize == obj->front)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void myCircularQueueFree(MyCircularQueue *obj)
{
    free(obj); //直接释放指针
}

队列的链式实现----链队

使用链表来实现队列时就不需要考虑队列满的情况了,一般来空间是足够的。

#include 
typedef int QElemType;
typedef struct QNode // 节点结构
{
    QElemType data;
    struct QNode *next;
} QNode, *QueuePtr;

typedef struct // 链表结构
{
    QueuePtr front, rear; // 头尾指针
} LinkQueue;
/**
* 入队
*/
int EnQueueu(LinkQueue *Q, QElemType e) 
{
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    if (!s) //存储分配失败
    {
        return -1;
    }
    s->data = e;
    s->next = NULL;    // 构造新节点
    Q->rear->next = s; // 让新节点成为队尾指针的下一个节点
    Q->rear = s;       // 把新节点设置为队尾节点
    return 1;
}
/**
* 出队
*/
int DeQueue(LinkQueue *Q, QElemType *e)
{
    if (Q->front == Q->rear)
    {
        return -1;
    }
    QueuePtr p;
    p = Q->front->next; // 暂存下要出队的节点
    *e = p->data;
    Q->front->next = p->next; //头指针指向新的队首节点

    if (Q->rear == p) // 这里是判断只有一个节点的队列删除节点后,应该将头尾指针指向同一地址
    {
        Q->rear = Q->front;
    }
    free(p); // 释放被删除节点
    return e;
}

两种实现的比较

从时间上,他们的基本操作都是O(1),不过循环队列是实现申请号确定的空间大小,使用期间不用释放,但对于链队列,每次申请和释放内存还是有一点时间开销。如果操作频繁,两者还是会有一点细微差异。
从空间上,循环队列必须有一个固定的空间大小,就会存在空间浪费的问题,但链队列就不存在这个问题。

你可能感兴趣的:(leetCode,leetCode,队列,C)