数据结构——栈和队列

一、栈的核心知识点

1、栈的核心定义与特性

  1. 核心操作限制

栈仅允许在表的一端进行插入(入栈)和删除(出栈)操作,这一端被称为栈顶(Top);而表的另一端是固定的,无法进行任何操作,被称为栈底(Bottom)。

栈底始终是第一个入栈的元素所在位置,栈顶则随元素的入栈、出栈动态移动。

  1. 核心原则:后进先出(LIFO,Last In First Out)

最后入栈的元素,一定会最先出栈;最先入栈的元素,只能最后出栈。

举例:元素按 1→2→3→4 顺序入栈,出栈顺序只能是 4→3→2→1;若入栈过程中穿插出栈(如1入栈→1出栈→2、3入栈→3出栈→4入栈),则出栈顺序为 1→3→4→2,仍符合"后进先出"。

  1. 栈的空/满状态

• 空栈:栈中没有任何元素,此时栈顶与栈底重合,是栈的初始状态。

• 满栈:针对静态栈(数组实现),栈的存储空间被元素占满,无法再执行入栈操作;动态栈(链表实现)无"满栈"概念,只要内存足够,可一直入栈。

2、栈的基本术语与操作

所有操作的时间复杂度均为O(1)(无循环/遍历),这是栈的高效性核心,以下是栈的基础操作(所有操作均围绕栈顶展开,无随机访问),且操作前需做合法性校验(如出栈/取栈顶前需判空,入栈前需判满(静态栈)):

|---------|-----------|------------------------|-----------------------------------------------------|-----------|
| 操作名称 | 英文标识 | 操作含义 | 核心实现逻辑 | 时间复杂度 |
| 初始化栈 | STInit | 创建一个空栈,初始化动态数组、栈顶指针和容量 | 将数组指针 a 置为 NULL,top(指向栈顶下一个位置)和 capacity 置为 0 | O(1) |
| 销毁栈 | STDestroy | 释放栈的动态数组内存,恢复为空栈 | 释放数组内存,重置指针、top 和 capacity | O(1) |
| 入栈 | STPush | 在栈顶插入新元素 | 检查容量,若已满则扩容(初始容量为4,后续扩容为2倍),将元素存入 a[top] 并将 top++ | O(1) (均摊) |
| 出栈 | STPop | 删除栈顶元素 | 校验栈非空,直接将 top--(无需销毁元素,覆盖即可) | O(1) |
| 获取栈顶元素 | STTop | 返回栈顶元素的值 | 校验栈非空,返回 a[top - 1] | O(1) |
| 判断栈是否为空 | STEmpty | 检查栈中是否无元素 | 返回 top == 0 的布尔值 | O(1) |
| 获取栈大小 | STSize | 返回栈中有效元素的个数 | 直接返回 top(因 top 指向栈顶下一个位置,其值即为元素个数) | O(1) |

3. 实现方式

操作合法性原则:空栈不可出栈、不可取栈顶;满栈不可入栈,违反则会出现"栈下溢"(空栈出栈)或"栈上溢"(满栈入栈)的错误。

二、栈的实现方式

栈的实现基于线性表的两种基础结构(数组、链表),两种方式各有优劣,适用于不同开发场景,核心要求均为保证入栈、出栈操作在栈顶完成,且时间复杂度O(1)。

数组实现(静态/动态数组栈)

核心设计

• 用数组存储栈的元素,数组的一端(通常为末尾)作为栈顶,另一端(数组起始位置[0])作为栈底。

• 用整型变量top作为栈顶指针,表示栈顶元素的下一个位置(常用设计),或直接指向栈顶元素;栈底指针固定为0,无需额外变量。

👉 推荐设计:top初始值为0(空栈,栈顶=栈底),入栈时arr[top] = 元素,再top++;出栈时top--,栈顶元素为arr[top-1]。

栈可以用数组或链表实现,数组实现更高效,因为栈顶操作对应数组的尾插尾删,时间复杂度为O(1)。

栈的完整C语言实现(动态数组版)

stack.h

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int STDataType;
typedef struct stack{
    STDataType* a;
    int top;
    int capacity;
}ST;

//初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);

//入栈
void STPush(ST* pst, STDataType x);
//出栈
void STPop(ST* pst);

//获取栈顶元素
STDataType STTop(ST* pst);

//判断栈是否为空
bool STEmpty(ST* pst);

//获取栈中元素个数
int STSize(ST* pst);

//打印栈中元素
void STPrint(ST* pst);

stack.c

cpp 复制代码
#include "stack.h"

// 初始化和销毁
void STInit(ST *pst)
{
    assert(pst);
    pst->a = NULL;
    // top指向栈顶数据的下一个位置,相当于size
    pst->top = 0;
    // top指向栈顶数据
    // pst->top = -1;
    pst->capacity = 0;
}
void STDestroy(ST *pst)
{
    assert(pst);
    free(pst->a);
    pst->a = NULL;
    pst->top = 0;
    pst->capacity = 0;
}

// 入栈和出栈
void STPush(ST *pst, STDataType x)
{
    assert(pst);
    //扩容
    if (pst->top == pst->capacity)
    {
        int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
        STDataType *tmp = (STDataType *)realloc(pst->a, newcapacity * sizeof(STDataType));
        if (tmp == NULL)
        {
            printf("realloc fail\n");
            exit(-1);
        }
        pst->a = tmp;
        pst->capacity = newcapacity;
    }
    pst->a[pst->top] = x;
    pst->top++;
}

void STPop(ST *pst)
{
    assert(pst);
    assert(pst->top > 0);//top=0时,栈为空
    pst->top--;
}

// 获取栈顶元素
STDataType STTop(ST *pst)
{
    assert(pst);
    assert(pst->top > 0);
    return pst->a[pst->top - 1];
}

// 判断栈是否为空
bool STEmpty(ST *pst)
{
    assert(pst);
    return pst->top == 0;
}

// 获取栈中元素个数
int STSize(ST *pst)
{
    assert(pst);
    return pst->top;
}

test.c

cpp 复制代码
#include "stack.h"

int main()
{
    ST st;
    STInit(&st);
    STPush(&st, 1);
    STPush(&st, 2);
    STPush(&st, 3);
    STPush(&st, 4);
    STPush(&st, 5);
    printf("top=%d\n", STTop(&st));
    STPop(&st);
    printf("top=%d\n", STTop(&st));
    STPop(&st);
    printf("top=%d\n", STTop(&st));
    int size = STSize(&st);
    printf("size=%d\n", size);
    while(!STEmpty(&st))
    {
        printf("%d ", STTop(&st));
        STPop(&st);
    }
    if (STEmpty(&st))
    {
        printf("stack is empty\n");
    }
    STDestroy(&st);
    return 0;
}

三、栈的核心特性延伸:无随机访问

栈作为受限线性表,与普通数组/链表的核心区别是不支持随机访问:

• 普通数组/链表可通过下标/指针直接访问任意位置的元素(如arr[5]、node->next->next);

• 栈只能访问栈顶元素,若要访问栈中其他位置的元素,必须先将其上方的所有元素依次出栈,直到目标元素成为栈顶,且访问后无法恢复原栈结构(除非重新入栈)。

✅ 例:栈中元素为1(底)→2→3→4(顶),要访问元素2,需先出栈4、3,使2成为栈顶;访问后,栈中仅剩1、2,若要恢复原栈,需重新入栈3、4。

四、栈的关键注意事项

  1. 数组栈的扩容策略:动态数组栈的初始容量不宜过大/过小,通常初始容量为4/8,满栈时扩容为原容量的2倍(平衡扩容开销和内存浪费)。

  2. 链表栈的节点释放:出栈时必须释放节点的内存,避免内存泄漏;销毁栈时需逐个释放所有节点,不能仅释放栈顶指针。

  3. 栈顶指针的设计:数组栈的top指针有两种设计(指向栈顶元素/栈顶下一个位置),项目中需保持统一,避免逻辑混乱。

  4. 空栈校验:所有对栈顶的操作(出栈、取栈顶)前,必须先判断栈是否为空,否则会导致野指针访问(链表栈)或数组越界(数组栈)。

  5. 元素类型的通用性:实际开发中,可通过C语言的typedef或C++的模板定义栈的元素类型,使栈支持任意数据类型(如int、char、结构体),提高复用性。

三、队列的核心知识点

1. 队列的核心定义与特性

  1. 核心操作限制

• 队列仅允许在队尾(Rear)进行插入操作(入队),在队头(Front)进行删除操作(出队)。

• 队头是第一个入队的元素所在位置,队尾是最后一个入队的元素所在位置。

  1. 核心原则:先进先出(FIFO,First In First Out)

最先入队的元素,一定会最先出队;最后入队的元素,只能最后出队。

举例:元素按 1→2→3→4 顺序入队,出队顺序只能是 1→2→3→4;若入队过程中穿插出队(如1入队→1出队→2、3入队→2出队→4入队),则出队顺序为 1→2→4→3,仍符合"先进先出"。

  1. 队列的空/满状态

• 空队列:队列中没有任何元素,此时队头与队尾重合,是队列的初始状态。

• 满队列:针对静态队列(数组实现),队列的存储空间被元素占满,无法再执行入队操作;动态队列(链表实现)无"满队列"概念,只要内存足够,可一直入队。

2. 队列的基本术语与操作

所有操作的时间复杂度均为O(1)(无循环/遍历),这是队列的高效性核心。以下是队列的基础操作(所有操作均围绕队头和队尾展开,无随机访问),且操作前需做合法性校验(如出队/取队头前需判空,入队前需判满(静态队列)):

|----------|--------------|-------------------------|-----------------------------------------------------|-------|
| 操作名称 | 英文标识 | 操作含义 | 核心实现逻辑 | 时间复杂度 |
| 初始化队列 | QueueInit | 创建一个空队列,初始化队头、队尾指针和元素个数 | 将 phead 和 ptail 置为 NULL,size 置为 0 | O(1) |
| 销毁队列 | QueueDestroy | 释放队列所有节点的内存,恢复为空队列 | 遍历链表,逐个释放节点,最后重置指针和 size | O(n) |
| 入队 | QueuePush | 在队尾插入新元素 | 为新节点分配内存,若队列为空则同时更新队头和队尾,否则链接到队尾并更新队尾指针,size++ | O(1) |
| 出队 | QueuePop | 删除队头元素 | 若队列只有一个节点,直接释放并重置指针;否则保存队头的下一个节点,释放队头并更新队头指针,size-- | O(1) |
| 获取队头元素 | QueueFront | 返回队头元素的值 | 直接返回队头节点的 val | O(1) |
| 获取队尾元素 | QueueBack | 返回队尾元素的值 | 直接返回队尾节点的 val | O(1) |
| 判断队列是否为空 | QueueEmpty | 检查队列是否无元素 | 返回 size == 0 的布尔值 | O(1) |
| 获取队列大小 | QueueSize | 返回队列中有效元素的个数 | 直接返回 size | O(1) |
| 遍历打印队列 | QueuePrint | 从队头到队尾打印所有元素 | 遍历链表,依次打印每个节点的 val | O(n) |

操作合法性原则:空队列不可出队、不可取队头/队尾;满队列不可入队,违反则会出现"队列下溢"(空队列出队)或"队列上溢"(满队列入队)的错误。

3. 队列的两种经典实现方式

队列的实现基于线性表的两种基础结构(数组、链表),两种方式各有优劣,适用于不同开发场景,核心要求均为保证入队、出队操作在队尾/队头完成,且时间复杂度O(1)。

链表实现(链表队列)

核心设计

• 用单链表存储队列的元素,链表的头节点作为队头,链表的尾节点作为队尾。

• 入队:尾插法(在链表尾部插入新节点),新节点成为新队尾;出队:头删法(删除链表头部节点),原第二个节点成为新队头。

• 为了保证尾插操作的时间复杂度为O(1),需要维护一个队尾指针(ptail),直接指向链表的尾节点。

队列用链表实现更高效,因为链表的头删和尾插操作时间复杂度为O(1),而数组实现的队列在出队时需要移动元素,效率较低。

数组实现(循环队列)

核心设计

• 用数组存储队列的元素,通过取模运算让数组首尾相连,解决普通数组队列的"假溢出"问题。

• 用两个指针 front(队头)和 rear(队尾)表示队列的范围,front 指向队头元素,rear 指向队尾元素的下一个位置。

• 队空条件:front == rear

• 队满条件:(rear + 1) % capacity == front(预留一个空位区分空/满)

• 有效元素个数:(rear - front + capacity) % capacity

4. 队列的核心特性延伸:无随机访问

队列作为受限线性表,与普通数组/链表的核心区别是不支持随机访问:

• 普通数组/链表可通过下标/指针直接访问任意位置的元素(如 arr[5]、node->next->next);

• 队列只能访问队头和队尾元素,若要访问队列中其他位置的元素,必须先将其前方的所有元素依次出队,直到目标元素成为队头,且访问后无法恢复原队列结构(除非重新入队)。

✅ 例:队列中元素为 1(头)→2→3→4(尾),要访问元素3,需先出队1、2,使3成为队头;访问后,队列中仅剩3、4,若要恢复原队列,需重新入队1、2。

四、队列的完整C语言实现(链表版)

Queue.h

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int QDataType;

typedef struct QueueNode
{
    struct QueueNode *next;
    QDataType val;
} QNode;

typedef struct Queue
{
    QNode *phead;
    QNode *ptail;
    int size;
} Queue;

void QueueInit(Queue *pq); // 初始化

void QueueDestroy(Queue *pq); // 销毁

void QueuePush(Queue *pq, QDataType x); // 入队,队尾插入

void QueuePop(Queue *pq); // 出队,队头删除

QDataType QueueFront(Queue *pq); // 获取队头元素
QDataType QueueBack(Queue *pq); // 获取队尾元素

bool QueueEmpty(Queue *pq); // 判断队列是否为空

void QueuePrint(Queue *pq); // 打印队列

int QueueSize(Queue *pq); // 返回队列中元素个数

Queue.c

cpp 复制代码
#include "Queue.h"

// QueueCreate 创建队列
void QueueInit(Queue *pq)
{
    assert(pq);
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}

// QueueDestroy 销毁队列
void QueueDestroy(Queue *pq)
{
    assert(pq);
    QNode *cur = pq->phead;
    while (cur)
    {
        QNode *next = cur->next;
        free(cur);
        cur = next;
    }
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}

// QueuePush 队尾插入
void QueuePush(Queue *pq, QDataType x)
{
    assert(pq);
    QNode *newnode = (QNode *)malloc(sizeof(QNode));
    if (newnode == NULL)
    {
        perror("malloc fail");
        exit(-1);
    }
    newnode->val = x;
    newnode->next = NULL;
    if (pq->ptail == NULL)
    {
        pq->phead = pq->ptail = newnode;
    }
    else
    {
        pq->ptail->next = newnode;
        pq->ptail = newnode;
    }
    pq->size++;
}

// QueuePop 队头删除
void QueuePop(Queue *pq)
{
    assert(pq);
    assert(pq->size != 0);
    //    QNode *next=pq->phead->next;
    //    free(pq->phead);
    //    pq->phead=next;
    //    if(pq->phead==NULL)
    //    {
    //        pq->ptail=NULL;
    //    }
    if (pq->phead->next == NULL) // 如果只有一个节点
    {
        free(pq->phead);
        pq->phead = pq->ptail = NULL;
    }
    else // 多个节点
    {
        QNode *next = pq->phead->next;
        free(pq->phead);
        pq->phead = next;
    }
    pq->size--;
}

// QueueFront 返回队头元素
QDataType QueueFront(Queue *pq)
{
    assert(pq);
    assert(pq->phead);
    return pq->phead->val;
}

// QueueBack 返回队尾元素
QDataType QueueBack(Queue *pq)
{
    assert(pq);
    assert(pq->ptail);
    return pq->ptail->val;
}

// QueueEmpty 判断队列是否为空
bool QueueEmpty(Queue *pq)
{
    assert(pq);
    return pq->size == 0;
}

// 返回元素个数
int QueueSize(Queue *pq)
{
    assert(pq);
    return pq->size;
}

// 遍历队列
void QueuePrint(Queue *pq)
{
    assert(pq);
    QNode *cur = pq->phead;
    while (cur)
    {
        printf("%d ", cur->val);
        cur = cur->next;
    }
    printf("\n");
}

test.c

cpp 复制代码
#include "Queue.h"
//gcc test.c Queue.c -o queue_test
//./queue_test
int main()
{
    Queue q;
    QueueInit(&q);
    QueuePush(&q, 1);
    QueuePush(&q, 2);
    QueuePush(&q, 3);
    QueuePush(&q, 4);
    QueuePrint(&q); // 1 2 3 4
    QueuePop(&q);
    QueuePop(&q);
    QueuePrint(&q); // 3 4

    QDataType front = QueueFront(&q);
    printf("%d\n", front);  // 3
    QDataType back = QueueBack(&q);
    printf("%d\n", back);  // 4
    printf("%d\n", QueueSize(&q)); // 2
    printf("%d\n", QueueEmpty(&q)); // 0
    QueueDestroy(&q);
    return 0;
}

五、循环队列核心知识点

  1. 核心原理

• 循环队列是数组实现队列的优化方案,通过取模运算让数组首尾相连,解决"假溢出"问题。

• 队空条件:front == rear

• 队满条件:(rear + 1) % N == front(预留一个空位区分空/满)

• 有效元素个数:(rear - front + N) % N

六、经典例题

1. 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

--左括号必须用相同类型的右括号闭合。

--左括号必须以正确的顺序闭合。

--每个右括号都有一个对应的相同类型的左括号。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int STDataType;
typedef struct Stack {
    STDataType* _a;
    int _top;      // 栈顶:指向栈顶下一个位置
    int _capacity; // 容量
} Stack;

// 初始化栈
void StackInit(Stack* ps) {
    assert(ps);
    ps->_a = NULL;
    ps->_top = ps->_capacity = 0;
}

// 入栈
void StackPush(Stack* ps, STDataType data) {
    assert(ps);
    if (ps->_top == ps->_capacity) {
        int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
        STDataType* tmp = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
        if (tmp == NULL) {
            printf("realloc fail\n");
            exit(-1);
        }
        ps->_a = tmp;
        ps->_capacity = newcapacity;
    }
    ps->_a[ps->_top++] = data;
}

// 出栈
void StackPop(Stack* ps) {
    assert(ps);
    assert(ps->_top > 0);
    ps->_top--;
}

// 获取栈顶元素
STDataType StackTop(Stack* ps) {
    assert(ps);
    assert(ps->_top > 0);
    return ps->_a[ps->_top - 1];
}

// 获取有效元素个数
int StackSize(Stack* ps) {
    assert(ps);
    return ps->_top;
}

// 检测栈是否为空
int StackEmpty(Stack* ps) {
    assert(ps);
    return ps->_top == 0;
}

// 销毁栈
void StackDestroy(Stack* ps) {
    assert(ps);
    free(ps->_a);
    ps->_a = NULL;
    ps->_top = ps->_capacity = 0;
}

// 有效括号判断
bool isValid(char* s) {
    Stack st;
    StackInit(&st);
    while (*s) { // 遍历字符串直到结束符'\0'
        if (*s == '(' || *s == '[' || *s == '{') {
            StackPush(&st, *s); // 左括号入栈
        } else {
            // 右括号匹配时栈空,直接不合法
            if (StackEmpty(&st)) {
                StackDestroy(&st);
                return false;
            }
            STDataType top = StackTop(&st);
            // 括号类型匹配则出栈
            if (*s == ')' && top == '(' || *s == ']' && top == '[' || *s == '}' && top == '{') {
                StackPop(&st);
            } else {
                // 类型不匹配,直接不合法
                StackDestroy(&st);
                return false;
            }
        }
        s++; // 移动指针,遍历下一个字符
    }
    // 将栈判空/销毁/返回移到循环外部,遍历完所有字符再判断
    bool ret = StackEmpty(&st); // 栈空=所有括号匹配,非空=有左括号残留
    StackDestroy(&st); // 无论结果如何,最终都销毁栈
    return ret;
}

// 测试主函数:验证不同用例
int main() {
    char s1[] = "()[]{}";  // 合法 → 1
    char s2[] = "([)]";    // 不合法 → 0
    char s3[] = "((()))";  // 合法 → 1
    char s4[] = "{";       // 不合法 → 0
    char s5[] = "}{";      // 不合法 → 0

    printf("s1: %d\n", isValid(s1));
    printf("s2: %d\n", isValid(s2));
    printf("s3: %d\n", isValid(s3));
    printf("s4: %d\n", isValid(s4));
    printf("s5: %d\n", isValid(s5));

    return 0;
}

有效括号问题的核心逻辑(栈的经典应用)

利用栈后进先出的特性,完美匹配括号的嵌套/并列规则:

• 左括号:无脑入栈,等待后续右括号匹配;

• 右括号:先判栈空(无左括号匹配→不合法),再判类型(类型不匹配→不合法),匹配则出栈;

• 遍历结束:栈空=所有左括号都有对应右括号,栈非空=有左括号残留→不合法。

2. 用队列实现栈

实现一个栈,该栈的所有操作(入栈、出栈、获取栈顶、判空)仅通过两个队列的基本操作完成(队列仅支持:队尾插入、队头删除、获取队头元素、判空、获取大小等基础操作)。

需实现的栈结构与接口

定义栈结构 MyStack,并实现以下核心函数,函数签名固定:

  1. 创建栈:MyStack* myStackCreate();

◦ 功能:初始化栈的结构,创建并初始化两个底层队列,返回栈的指针。

  1. 入栈:void myStackPush(MyStack* obj, int x);

◦ 功能:将元素 x 压入栈顶,仅通过队列操作实现。

  1. 出栈:int myStackPop(MyStack* obj);

◦ 功能:删除并返回栈顶元素,仅通过队列操作实现;栈非空时调用。

  1. 获取栈顶:int myStackTop(MyStack* obj);

◦ 功能:仅返回栈顶元素(不删除),仅通过队列操作实现;栈非空时调用。

  1. 判空:bool myStackEmpty(MyStack* obj);

◦ 功能:判断栈是否为空,返回布尔值(空:true,非空:false)。

  1. 销毁栈:void myStackFree(MyStack* obj);

◦ 功能:释放栈及底层两个队列的所有内存,避免内存泄漏。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 定义队列存储的数据类型
typedef int QDataType;

// 定义队列节点结构
typedef struct QueueNode
{
    struct QueueNode *next;  // 指向下一个节点的指针
    QDataType val;           // 节点存储的数据
} QNode;

// 定义队列结构(带头尾指针+大小,方便操作)
typedef struct Queue
{
    QNode *phead;  // 队列头指针
    QNode *ptail;  // 队列尾指针
    int size;      // 队列当前元素个数
} Queue;

// 初始化队列:传入队列地址,初始化头尾指针和大小
void QueueInit(Queue *pq)
{
    assert(pq);  // 断言保证队列地址非空
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}

// 销毁队列:释放所有节点内存,重置队列状态
void QueueDestroy(Queue *pq)
{
    assert(pq);  // 断言保证队列地址非空
    QNode *cur = pq->phead;  // 从队头开始遍历
    while (cur)
    {
        QNode *next = cur->next;  // 保存下一个节点地址,避免释放后丢失
        free(cur);                // 释放当前节点
        cur = next;               // 遍历下一个节点
    }
    pq->phead = pq->ptail = NULL; // 重置头尾指针
    pq->size = 0;                 // 重置元素个数
}

// 队尾入队:在队列尾部插入新元素
void QueuePush(Queue *pq, QDataType x)
{
    assert(pq);  // 断言保证队列地址非空
    // 开辟新节点内存
    QNode *newnode = (QNode *)malloc(sizeof(QNode));
    if (newnode == NULL)  // 检查内存开辟是否成功
    {
        perror("malloc fail");
        exit(-1);
    }
    newnode->val = x;     // 给新节点赋值
    newnode->next = NULL; // 新节点作为队尾,next置空

    if (pq->ptail == NULL) // 队列空的情况:头尾指针都指向新节点
    {
        pq->phead = pq->ptail = newnode;
    }
    else // 队列非空:尾节点next指向新节点,更新尾指针
    {
        pq->ptail->next = newnode;
        pq->ptail = newnode;
    }
    pq->size++; // 队列元素个数+1
}

// 队头出队:删除队列头部的元素
void QueuePop(Queue *pq)
{
    assert(pq);
    assert(pq->size != 0); // 断言保证队列非空,避免空队列删除

    if (pq->phead->next == NULL) // 队列只有一个节点的情况
    {
        free(pq->phead);         // 释放唯一节点
        pq->phead = pq->ptail = NULL; // 头尾指针置空
    }
    else // 队列有多个节点的情况
    {
        QNode *next = pq->phead->next; // 保存第二个节点地址
        free(pq->phead);               // 释放队头节点
        pq->phead = next;              // 头指针指向新的队头
    }
    pq->size--; // 队列元素个数-1
}

// 获取队头元素:返回队列头部的元素值(不删除)
QDataType QueueFront(Queue *pq)
{
    assert(pq);
    assert(pq->phead); // 断言保证队列非空
    return pq->phead->val;
}

// 获取队尾元素:返回队列尾部的元素值(不删除)
QDataType QueueBack(Queue *pq)
{
    assert(pq);
    assert(pq->ptail); // 断言保证队列非空
    return pq->ptail->val;
}

// 判断队列是否为空:空返回true,非空返回false
bool QueueEmpty(Queue *pq)
{
    assert(pq);
    return pq->size == 0; // 通过元素个数判断,更直观
}

// 获取队列元素个数:返回当前队列的节点数
int QueueSize(Queue *pq)
{
    assert(pq);
    return pq->size;
}

// 遍历打印队列:从队头到队尾输出所有元素
void QueuePrint(Queue *pq)
{
    assert(pq);
    QNode *cur = pq->phead; // 从队头开始遍历
    while (cur)
    {
        printf("%d ", cur->val); // 打印当前节点值
        cur = cur->next;         // 遍历下一个节点
    }
    printf("\n"); // 换行,美化输出
}

// 用两个队列实现栈的结构定义
typedef struct {
    Queue *q1;  // 主队列:始终存储栈的所有元素,队头=栈顶
    Queue *q2;  // 辅助队列:入栈时临时存储,完成后置空
} MyStack;

// 创建并初始化栈:开辟栈内存,初始化两个底层队列
MyStack* myStackCreate() {
    // 开辟栈的内存空间
    MyStack* stack = (MyStack*)malloc(sizeof(MyStack));
    // 为两个队列分别开辟内存
    stack->q1 = (Queue*)malloc(sizeof(Queue));
    stack->q2 = (Queue*)malloc(sizeof(Queue));
    // 初始化两个队列的头尾指针和大小
    QueueInit(stack->q1);
    QueueInit(stack->q2);
    return stack; // 返回栈的指针
}

// 栈的入栈操作:将元素x压入栈顶(核心:利用辅助队列实现队列转栈)
void myStackPush(MyStack* obj, int x) {
    // 第一步:新元素先入队到辅助队列q2(队尾入)
    QueuePush(obj->q2, x);
    // 第二步:将主队列q1的所有元素,依次出队并入队到q2
    while(!QueueEmpty(obj->q1))
    {
        QueuePush(obj->q2, QueueFront(obj->q1)); // q1队头元素入q2队尾
        QueuePop(obj->q1);                       // 弹出q1的队头元素,避免数据残留
    }
    // 第三步:交换q1和q2的指向,让q2成为新的空辅助队列,q1成为主队列
    // 此时q1的队头就是刚入栈的元素(栈顶),实现队列→栈的特性转换
    Queue* t = obj->q1;
    obj->q1 = obj->q2;
    obj->q2 = t;
}

// 栈的出栈操作:删除并返回栈顶元素(q1队头=栈顶,直接操作q1即可)
int myStackPop(MyStack* obj) {
   // 第一步:先获取q1队头元素(即栈顶元素),保存到临时变量
   int top = QueueFront(obj->q1);
   // 第二步:删除q1的队头元素(完成栈顶的删除操作)
   QueuePop(obj->q1);
   // 第三步:返回保存的栈顶元素
   return top;
}

// 获取栈顶元素:返回栈顶值(不删除,直接取q1队头)
int myStackTop(MyStack* obj) {
    return QueueFront(obj->q1); // q1队头始终是栈顶,直接返回
}

// 判断栈是否为空:空返回true,非空返回false
bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(obj->q1); // 主队列q1为空,则栈为空
}

// 销毁栈:释放栈和两个队列的所有内存,避免内存泄漏
void myStackFree(MyStack* obj) {
    // 第一步:销毁两个队列的节点内存
    QueueDestroy(obj->q1);
    QueueDestroy(obj->q2);
    // 第二步:释放两个队列本身的内存
    free(obj->q1);
    free(obj->q2);
    // 第三步:释放栈结构的内存
    free(obj);
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();  // 创建栈
 * myStackPush(obj, x);             // 入栈元素x
 * int param_2 = myStackPop(obj);   // 出栈,返回栈顶元素
 * int param_3 = myStackTop(obj);   // 获取栈顶元素
 * bool param_4 = myStackEmpty(obj);// 判断栈是否为空
 * myStackFree(obj);                // 销毁栈,释放内存
*/

核心思路

  1. 两个队列配合:

◦ q1 始终保存栈的所有元素,且队头就是栈顶元素,这样 pop 和 top 操作可以直接在 O(1) 时间完成。

◦ q2 仅在 push 时使用,用来临时存储新元素,再把 q1 的元素转移过来,保证新元素在队头。

  1. 入栈逻辑:

◦ 新元素先入队到 q2,然后把 q1 的所有元素依次出队并入队到 q2。

◦ 交换 q1 和 q2,让 q1 成为新的主队列,此时 q1 的队头就是刚入栈的元素。

关键注释重点说明

  1. 队列核心操作:标注了内存开辟检查、空队列/单节点队列边界处理,避免野指针和内存泄漏;

  2. 入栈核心逻辑:分三步标注新元素入辅助队列、主队列元素转移、队列交换,清晰解释"队列转栈"的原理;

  3. 出栈核心逻辑:标注先取值再删除的原因(因为QueuePop是void类型,无返回值);

  4. 内存管理:在Destroy/Free函数中,标注了先释放节点、再释放结构体的顺序,避免内存泄漏。

方法二:

cpp 复制代码
typedef int QDataType;

typedef struct QueueNode
{
    struct QueueNode *next;
    QDataType val;
} QNode;

typedef struct Queue
{
    QNode *phead;
    QNode *ptail;
    int size;
} Queue;

// QueueCreate 创建队列
void QueueInit(Queue *pq)
{
    assert(pq);
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}

// QueueDestroy 销毁队列
void QueueDestroy(Queue *pq)
{
    assert(pq);
    QNode *cur = pq->phead;
    while (cur)
    {
        QNode *next = cur->next;
        free(cur);
        cur = next;
    }
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}

// QueuePush 队尾插入
void QueuePush(Queue *pq, QDataType x)
{
    assert(pq);
    QNode *newnode = (QNode *)malloc(sizeof(QNode));
    if (newnode == NULL)
    {
        perror("malloc fail");
        exit(-1);
    }
    newnode->val = x;
    newnode->next = NULL;
    if (pq->ptail == NULL)
    {
        pq->phead = pq->ptail = newnode;
    }
    else
    {
        pq->ptail->next = newnode;
        pq->ptail = newnode;
    }
    pq->size++;
}

// QueuePop 队头删除
void QueuePop(Queue *pq)
{
    assert(pq);
    assert(pq->size != 0);
    //    QNode *next=pq->phead->next;
    //    free(pq->phead);
    //    pq->phead=next;
    //    if(pq->phead==NULL)
    //    {
    //        pq->ptail=NULL;
    //    }
    if (pq->phead->next == NULL) // 如果只有一个节点
    {
        free(pq->phead);
        pq->phead = pq->ptail = NULL;
    }
    else // 多个节点
    {
        QNode *next = pq->phead->next;
        free(pq->phead);
        pq->phead = next;
    }
    pq->size--;
}

// QueueFront 返回队头元素
QDataType QueueFront(Queue *pq)
{
    assert(pq);
    assert(pq->phead);
    return pq->phead->val;
}

// QueueBack 返回队尾元素
QDataType QueueBack(Queue *pq)
{
    assert(pq);
    assert(pq->ptail);
    return pq->ptail->val;
}

// QueueEmpty 判断队列是否为空
bool QueueEmpty(Queue *pq)
{
    assert(pq);
    return pq->size == 0;
}

// 返回元素个数
int QueueSize(Queue *pq)
{
    assert(pq);
    return pq->size;
}

// 遍历队列
void QueuePrint(Queue *pq)
{
    assert(pq);
    QNode *cur = pq->phead;
    while (cur)
    {
        printf("%d ", cur->val);
        cur = cur->next;
    }
    printf("\n");
}


typedef struct {
    Queue *q1;
    Queue *q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* stack = (MyStack*)malloc(sizeof(MyStack));
    stack->q1 = (Queue*)malloc(sizeof(Queue));
    stack->q2 = (Queue*)malloc(sizeof(Queue));
    // 初始化队列
    QueueInit(stack->q1);
    QueueInit(stack->q2);
    return stack;
}

void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(obj->q1))
    {
        QueuePush(obj->q1,x);
    }
    else
    {
        QueuePush(obj->q2,x);
    }
}

int myStackPop(MyStack* obj) {
    //假设法
    Queue* empty = obj->q1;
    Queue* nonempty = obj->q2;
    if(!QueueEmpty(obj->q1))
    {
        empty = obj->q2;
        nonempty = obj->q1;
    }
    //不为空前size-1导走,删除最后一个就是栈顶数据
    while(QueueSize(nonempty)>1)
    {
        QueuePush(empty,QueueFront(nonempty));
        QueuePop(nonempty);
    }

    int top = QueueFront(nonempty);
    QueuePop(nonempty);
    return top;
}

int myStackTop(MyStack* obj) {
    if(!QueueEmpty(obj->q1))
    {
        return QueueBack(obj->q1);
    }
    else
    {
        return QueueBack(obj->q2);
    }
    
}

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(obj->q1) && QueueEmpty(obj->q2);
}

void myStackFree(MyStack* obj) {
    QueueDestroy(obj->q1);
    QueueDestroy(obj->q2);
    free(obj->q1);
    free(obj->q2);
    free(obj);
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

3. 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 只能 使用标准的栈操作 ------ 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
cpp 复制代码
typedef int STDataType;
typedef struct stack{
    STDataType* a;
    int top;
    int capacity;
}ST;

// 初始化和销毁
void STInit(ST *pst)
{
    assert(pst);
    pst->a = NULL;
    // top指向栈顶数据的下一个位置,相当于size
    pst->top = 0;
    // top指向栈顶数据
    // pst->top = -1;
    pst->capacity = 0;
}
void STDestroy(ST *pst)
{
    assert(pst);
    free(pst->a);
    pst->a = NULL;
    pst->top = 0;
    pst->capacity = 0;
}

// 入栈和出栈
void STPush(ST *pst, STDataType x)
{
    assert(pst);
    //扩容
    if (pst->top == pst->capacity)
    {
        int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
        STDataType *tmp = (STDataType *)realloc(pst->a, newcapacity * sizeof(STDataType));
        if (tmp == NULL)
        {
            printf("realloc fail\n");
            exit(-1);
        }
        pst->a = tmp;
        pst->capacity = newcapacity;
    }
    pst->a[pst->top] = x;
    pst->top++;
}

void STPop(ST *pst)
{
    assert(pst);
    assert(pst->top > 0);//top=0时,栈为空
    pst->top--;
}

// 获取栈顶元素
STDataType STTop(ST *pst)
{
    assert(pst);
    assert(pst->top > 0);
    return pst->a[pst->top - 1];
}

// 判断栈是否为空
bool STEmpty(ST *pst)
{
    assert(pst);
    return pst->top == 0;
}

// 获取栈中元素个数
int STSize(ST *pst)
{
    assert(pst);
    return pst->top;
}


typedef struct {
    ST pushst;
    ST popst;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue *obj=(MyQueue*)malloc(sizeof(MyQueue));
    STInit(&(obj->pushst));
    STInit(&(obj->popst));
    return obj;
}

void myQueuePush(MyQueue* obj, int x) {
    STPush(&(obj->pushst),x);
}

int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&(obj->popst)))
    {
        //倒数据
        while(!STEmpty(&(obj->pushst)))
        {
            int top=STTop(&(obj->pushst));
            STPush(&(obj->popst),top);
            STPop(&(obj->pushst));
        }
    }
    return STTop(&(obj->popst));
}

int myQueuePop(MyQueue* obj) {
    int front=myQueuePeek(obj);
    STPop(&(obj->popst));
    return front;
}


bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&(obj->popst)) && STEmpty(&(obj->pushst));
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&(obj->popst));
    STDestroy(&(obj->pushst));
    free(obj);
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

本题核心思路是利用两个栈的"先进后出"特性,通过数据倒换模拟队列的"先进先出"特性,一个栈专门负责入队(push栈),一个栈专门负责出队(pop栈),仅在pop栈为空时,将push栈的所有数据倒入pop栈,实现顺序反转。

一、核心原理

栈的特性是先进后出,队列是先进先出;将一个栈的所有元素倒入另一个栈,元素的访问顺序会被反转,两次"先进后出"即可实现"先进先出",这是本题的核心逻辑。

二、数据结构设计

  1. 先实现通用的栈结构:包含动态数组(a)、栈顶指针(top,指向栈顶下一个位置)、容量(capacity),并封装栈的初始化、销毁、入栈、出栈、取栈顶、判空、获取元素个数等基础操作。

  2. 队列结构(MyQueue):嵌套两个栈,pushst 负责接收入队元素,popst 负责提供出队/取队首元素,两个栈配合完成队列所有操作。

三、各接口实现思路

  1. 队列创建(myQueueCreate)

• 为队列结构体(MyQueue)申请堆内存,避免栈上内存销毁问题。

• 分别初始化队列内的pushst和popst两个栈,保证栈的初始状态(数组空、top=0、capacity=0)。

  1. 入队(myQueuePush)

• 入队操作直接复用栈的入栈功能,将元素直接压入push栈,时间复杂度O(1)。

• 无需操作pop栈,减少不必要的性能消耗。

  1. 取队首元素(myQueuePeek)

这是核心接口,实现push栈到pop栈的按需倒数据(仅pop栈空时倒):

  1. 先判断pop栈是否为空,若为空则执行数据倒换;若不为空,直接返回pop栈的栈顶(即队列首元素)。

  2. 倒数据:循环将push栈的栈顶元素压入pop栈,同时弹出push栈的栈顶,直到push栈为空。

  3. 数据倒换后,pop栈的栈顶就是队列的首元素,直接返回pop栈栈顶即可。

• 倒数据的时间复杂度为O(n),但每个元素仅被倒换一次,均摊时间复杂度仍为O(1)。

  1. 出队(myQueuePop)

• 直接复用myQueuePeek接口:先通过myQueuePeek保证pop栈有数据,且获取到队首元素。

• 对pop栈执行出栈操作(弹出栈顶),完成队列的出队,最后返回获取到的队首元素。

  1. 判空(myQueueEmpty)

• 队列的空状态要求两个栈同时为空:若push栈有元素但pop栈空,仅需倒数据即可获取队首/出队,队列并非真正为空。

  1. 队列销毁(myQueueFree)

• 先分别销毁队列内的push栈和pop栈(释放栈的动态数组,重置栈的参数)。

• 最后释放队列结构体本身的堆内存,避免内存泄漏。

四、关键优化点

  1. 按需倒数据:仅当pop栈为空时才将push栈的所有数据倒换,而非每次入队/出队都倒换,避免重复操作,保证性能。

  2. 动态栈实现:栈采用动态数组实现,支持自动扩容(初始容量4,满后2倍扩容),避免固定数组的容量限制,适配任意数量的元素入队。

  3. 接口解耦:队列的所有操作均复用栈的基础接口,降低代码耦合度,便于维护和修改。

五、特性总结

• 入队操作:O(1) 时间复杂度。

• 出队/取队首:均摊 O(1) 时间复杂度(每个元素仅倒换一次)。

• 空间复杂度:O(n),n为队列中元素个数,两个栈的总存储空间为n。

• 满足队列先进先出的核心特性,完全模拟队列的所有基础操作。

4. 设计循环队列

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

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

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

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

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3 circularQueue.enQueue(1); // 返回 true circularQueue.enQueue(2); // 返回 true circularQueue.enQueue(3); // 返回 true circularQueue.enQueue(4); // 返回 false,队列已满 circularQueue.Rear(); // 返回 3 circularQueue.isFull(); // 返回 true circularQueue.deQueue(); // 返回 true circularQueue.enQueue(4); // 返回 true circularQueue.Rear(); // 返回 4
**提示:**所有的值都在 0 至 1000 的范围内;操作数将在 1 至 1000 的范围内;请不要使用内置的队列库。

cpp 复制代码
typedef struct {
    int* a;
    int head,tail,k;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个解决栈溢出问题
    obj->a = malloc(sizeof(int)*(k+1));
    obj->head=obj->tail=0;
    obj->k=k;
    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
     return obj->head==obj->tail;
}

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

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

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

}

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
       return -1;
    return obj->a[(obj->tail-1+obj->k+1) % (obj->k+1)];
    // int rear=obj->tail == 0 ? k : obj->tail-1;
    // return rear;
}


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

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

数组实现循环队列的核心思路

这个实现的核心是用数组+双指针+多开1个空间来模拟队列的环形结构,解决普通数组队列"假溢出"和"空/满状态无法区分"的问题。
一、核心设计思路

  1. 底层存储

◦ 使用动态数组 a 作为底层存储。

◦ 为了区分队列的"空"和"满"状态,数组的实际容量是 k+1(k 是题目要求的队列容量)。

◦ 这样做避免了队列头尾指针重叠时,无法判断是队空还是队满的问题。

  1. 双指针控制

◦ head 指针:指向队头元素的下标。

◦ tail 指针:指向队尾的下一个可插入位置。

◦ 入队时,新元素放在 tail 位置,然后 tail 后移;出队时,head 直接后移。

  1. 环形复用机制

◦ 当 head 或 tail 到达数组末尾时,通过取模运算 % (k+1) 回到数组开头,实现数组的环形复用。

◦ 这避免了普通数组队列中"删除队头后,前面空间无法复用"的假溢出问题。

  1. 空/满状态判断

◦ 队空:head == tail(头尾指针重合,没有元素)。

◦ 队满:(tail + 1) % (k+1) == head(tail 的下一个位置是 head,预留一个空位区分空/满)。
二、各接口的实现思路

  1. 创建队列 myCircularQueueCreate

• 分配 MyCircularQueue 结构体的内存。

• 为数组 a 分配 k+1 个整型的空间。

• 初始化 head 和 tail 为 0,k 为传入的容量。

  1. 入队 myCircularQueueEnQueue

• 先调用 myCircularQueueIsFull 判断队列是否已满,满则返回 false。

• 将元素存入 a[tail],然后 tail 后移一位并取模。

• 返回 true 表示入队成功。

  1. 出队 myCircularQueueDeQueue

• 先调用 myCircularQueueIsEmpty 判断队列是否为空,空则返回 false。

• head 直接后移一位并取模,相当于删除队头元素。

• 返回 true 表示出队成功。

  1. 获取队头 myCircularQueueFront

• 队空时返回 -1,否则直接返回 a[head]。

  1. 获取队尾 myCircularQueueRear

• 队空时返回 -1。

• 队尾元素的下标是 (tail - 1 + k + 1) % (k+1)(处理 tail=0 的边界情况),返回该位置的值。

  1. 判空 myCircularQueueIsEmpty

• 直接判断 head == tail。

  1. 判满 myCircularQueueIsFull

• 判断 (tail + 1) % (k+1) == head。

  1. 销毁队列 myCircularQueueFree

• 先释放数组 a 的内存,再释放结构体 obj 的内存,避免内存泄漏。

相关推荐
只是懒得想了4 小时前
C++实现密码破解工具:从MD5暴力破解到现代哈希安全实践
c++·算法·安全·哈希算法
ruxshui4 小时前
个人笔记: 星环Inceptor/hive普通分区表与范围分区表核心技术总结
hive·hadoop·笔记
码农水水4 小时前
得物Java面试被问:消息队列的死信队列和重试机制
java·开发语言·jvm·数据结构·机器学习·面试·职场和发展
慾玄4 小时前
渗透笔记总结
笔记
m0_736919104 小时前
模板编译期图算法
开发语言·c++·算法
dyyx1114 小时前
基于C++的操作系统开发
开发语言·c++·算法
m0_736919104 小时前
C++安全编程指南
开发语言·c++·算法
CS创新实验室4 小时前
关于 Moltbot 的学习总结笔记
笔记·学习·clawdbot·molbot
蜡笔小马4 小时前
11.空间索引的艺术:Boost.Geometry R树实战解析
算法·r-tree
-Try hard-4 小时前
数据结构:链表常见的操作方法!!
数据结构·算法·链表·vim