leetcode刷题——设计循环链表

题目要求我们设计循环队列,其特点是容量固定,队列循环,如图所示:

这里的队列我们以链表队列举例,对于循环,只需要把尾节点的指针指向头节点。重点是队列的容量固定:如何确定队列是否已满和空,我们可能第一时间想到的是普通队列的尾指针因为扩容导致的移动,即下面的图片:当队尾节点的next指针指向的是队头指针的时候说明是容量已满状态

即:Queue->tail->next == Queue->head;

但是这么做会有问题,就是当只有一个元素的时候(k=1),这时队列头尾指针都指向这一个节点,所以无论是否存储,队尾节点的next指针都指向队头,还有,当出队列一直出到最后一个节点,这时也无法判断是否为空,因为这时 Queue->tail == Queue->head存在,空和有一个节点,判断条件都不会变化。

那我们就来解决无法判断空当 k=1 一个节点时无法判断是否为满的情况,让判断条件在不同情况有差异:

首先是区分判断空的两种情况,按照出队列的规律,剩一个数据和空的区别就是队头指针向后移动一个单位,(因为循环队头指针相当于循环了一圈)所以可以说当Queue->tail->next == Queue->head 的时候为空,这正好也是容量满的条件,为了区分这两种情况,我们可以新增一个节点(总共 k+1 个节点)不存储有效数据,区分这两种情况:

即Queue->tail->next == Queue->head 时说明容量已经满,
Queue->tail->next->next == Queue->head 时说明空。

第一张图片是已经满的情况,第二张图片是一直出队列直到为空的情况,分别对应两种情况,新增节点同样可以解决 k=1 的情况。

通俗的来解释就是新增了一个节点,当队尾节点两次next后才找到队头节点说明容量满了,当队尾节点一次next后就找到队头节点就说明(队头节点一直出队列甚至把队尾节点出掉)队列空了。

接下来是代码部分,首先是创建循环队列:

cpp 复制代码
struct QueueNode {
    int val;
    struct QueueNode* next;
};

typedef struct {
    struct QueueNode* head;
    struct QueueNode* tail;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    int count = k;
    struct QueueNode* cur_next = NULL;
    q->head = (struct QueueNode*)malloc(sizeof(struct QueueNode));
    struct QueueNode* cur = q->head;
    for (count; count > 0; count--) {
        cur_next = (struct QueueNode*)malloc(sizeof(struct QueueNode));
        cur->next = cur_next;
        cur = cur_next;
    }if (cur_next != NULL) {
        q->tail = cur_next;
        cur_next->next = q->head;
    }
    return q;
}

注意creat函数创建了 k+1 个节点,并且队头队尾指针指向同一节点。

接着是判断容量满和空的函数:

cpp 复制代码
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if (obj->tail->next == obj->head) {
        return true;
    }
    else {
        return false;
    }
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    if (obj->tail->next->next == obj->head) {
        return true;
    }
    else {
        return false;
    }
}

和刚才解释的条件一样。

然后是检查是否插入或删除成功的函数:

cpp 复制代码
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj)) {
        return false;
    }
    obj->tail = obj->tail->next;
    obj->tail->val = value;
    return true;
}

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

这里删除函数我是直接把队头指针向后next操作一下。

最后是返回队头函数和返回队尾元素函数:

cpp 复制代码
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;
}

直接返回即可。

最后不要忘记写free函数,这就是文章的全部内容了,解释的很烂还请谅解,如有错误欢迎指出。

相关推荐
先吃饱再说3 小时前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰6 小时前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术7 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六10 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术11 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize12 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考1 天前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode