数据结构-栈和队列

目录

栈的概念及结构

栈的实现

接口

动态栈(支持扩容)的结构定义

[1. 初始化栈](#1. 初始化栈)

[2. 入栈](#2. 入栈)

[3. 出栈](#3. 出栈)

[4. 获取栈顶元素](#4. 获取栈顶元素)

[5. 获取栈中有效元素个数](#5. 获取栈中有效元素个数)

[6. 检测栈是否为空](#6. 检测栈是否为空)

[7. 销毁栈](#7. 销毁栈)

代码总

队列

队列的概念及结构

队列的实现

接口

[1. 初始化队列](#1. 初始化队列)

[2. 队尾入队列](#2. 队尾入队列)

[3. 队头出队列](#3. 队头出队列)

[4. 获取队列头部元素](#4. 获取队列头部元素)

[5. 获取队列队尾元素](#5. 获取队列队尾元素)

[6. 获取队列中有效元素个数](#6. 获取队列中有效元素个数)

[7. 检测队列是否为空](#7. 检测队列是否为空)

[8. 销毁队列](#8. 销毁队列)

代码总

循环队列

为什么需要循环队列

循环队列的结构(数组实现)

区分队空和队满

方法一:牺牲一个存储单元

[方法二:增加一个 size 变量](#方法二:增加一个 size 变量)

循环队列的基本操作(以牺牲一个单元的方法为例)

[1. 初始化循环队列](#1. 初始化循环队列)

[2. 判断队列是否为空](#2. 判断队列是否为空)

[3. 判断队列是否已满](#3. 判断队列是否已满)

[4. 入队](#4. 入队)

[5. 出队](#5. 出队)

[6. 获取队头元素](#6. 获取队头元素)

[7. 获取队尾元素](#7. 获取队尾元素)

[8. 获取队列中元素个数](#8. 获取队列中元素个数)

[9. 销毁队列](#9. 销毁队列)

代码总

栈和队列面试题

概念选择题


栈的概念及结构

栈:一种特殊的线性表 ,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶

栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的 代价比较小

接口

cpp 复制代码
//下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
 STDataType* _a;
 int _top; // 栈顶
 int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps); 
// 入栈
void StackPush(Stack* ps, STDataType data); 
// 出栈
void StackPop(Stack* ps); 
// 获取栈顶元素
STDataType StackTop(Stack* ps); 
// 获取栈中有效元素个数
int StackSize(Stack* ps); 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps); 
// 销毁栈
void StackDestroy(Stack* ps);

动态栈(支持扩容)的结构定义

_top 表示下一个可以存放元素的位置,即栈中元素个数。

初始化时 _top = 0;入栈时先放入再 _top++;出栈时直接 _top--

_capacity 表示数组最多能容纳的元素个数。

_top == _capacity 时,需要扩容(通常 2 倍增长)。

cpp 复制代码
typedef int STDataType;      // 栈中元素类型,可修改

typedef struct Stack
{
    STDataType* _a;          // 指向动态开辟的数组
    int _top;                // 栈顶位置(通常指向栈顶元素的下一个位置)
    int _capacity;           // 当前已分配的容量
} Stack;

1. 初始化栈

解释

将栈结构体的指针、容量、栈顶都初始化为 0。

注意:_a 先置为 NULL,后续第一次入栈时再动态分配。

时间复杂度 O(1)。

cpp 复制代码
// 初始化栈
void StackInit(Stack* ps)
{
    assert(ps != NULL);
    ps->_a = NULL;
    ps->_top = 0;          // 栈顶初始为 0,表示没有元素
    ps->_capacity = 0;
}

2. 入栈

解释

将数据压入栈顶。

先检查是否需要扩容:若 _top == _capacity,则扩容(原容量为 0 时分配 4 个空间,否则翻倍)。

然后在 _a[_top] 处放入数据,_top++

cpp 复制代码
// 入栈
void StackPush(Stack* ps, STDataType data)
{
    assert(ps != NULL);
    // 检查容量,满了就扩容
    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)
        {
            perror("realloc fail");
            return;
        }
        ps->_a = tmp;
        ps->_capacity = newCapacity;
    }
    // 放入数据
    ps->_a[ps->_top] = data;
    ps->_top++;
}

3. 出栈

解释

删除栈顶元素。

只需将 _top-- 即可(逻辑删除)。

前提是栈非空,需断言检查。

cpp 复制代码
// 出栈
void StackPop(Stack* ps)
{
    assert(ps != NULL);
    assert(ps->_top > 0);      // 栈不能为空
    ps->_top--;
}

4. 获取栈顶元素

解释

返回栈顶元素的值(不是删除)。

由于 _top 指向栈顶元素的下一个位置,栈顶元素下标为 _top - 1

需确保栈非空。

cpp 复制代码
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
    assert(ps != NULL);
    assert(ps->_top > 0);
    return ps->_a[ps->_top - 1];
}

5. 获取栈中有效元素个数

解释

直接返回 _top 的值,即栈中元素个数。

cpp 复制代码
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
    assert(ps != NULL);
    return ps->_top;
}

6. 检测栈是否为空

解释

如果栈为空,返回非零(常用 1);如果不为空,返回 0。

实现上直接返回 _top == 0 的结果。

cpp 复制代码
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
    assert(ps != NULL);
    return ps->_top == 0 ? 1 : 0;   // 空返回1,非空返回0
}

7. 销毁栈

解释

释放动态申请的数组内存,并将指针置为 NULL,同时重置 _top_capacity

防止内存泄漏。

cpp 复制代码
// 销毁栈
void StackDestroy(Stack* ps)
{
    assert(ps != NULL);
    if (ps->_a != NULL)
    {
        free(ps->_a);
        ps->_a = NULL;
    }
    ps->_top = 0;
    ps->_capacity = 0;
}

代码总

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

typedef int QDataType;

// 队列结点
typedef struct QListNode
{
    struct QListNode* _pNext;
    QDataType _data;
} QNode;

// 队列结构(含头尾指针和元素个数)
typedef struct Queue
{
    QNode* _front;   // 队头指针
    QNode* _rear;    // 队尾指针
    int _size;       // 队列中元素个数
} Queue;

// 初始化队列
void QueueInit(Queue* q)
{
    assert(q != NULL);
    q->_front = NULL;
    q->_rear = NULL;
    q->_size = 0;
}

// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
    assert(q != NULL);
    QNode* newNode = (QNode*)malloc(sizeof(QNode));
    if (newNode == NULL)
    {
        perror("malloc fail");
        return;
    }
    newNode->_data = data;
    newNode->_pNext = NULL;

    if (q->_front == NULL)   // 空队列
    {
        q->_front = newNode;
        q->_rear = newNode;
    }
    else
    {
        q->_rear->_pNext = newNode;
        q->_rear = newNode;
    }
    q->_size++;
}

// 队头出队列
void QueuePop(Queue* q)
{
    assert(q != NULL);
    assert(q->_front != NULL);   // 队列非空
    QNode* del = q->_front;
    q->_front = q->_front->_pNext;
    if (q->_front == NULL)       // 删除后队列为空
        q->_rear = NULL;
    free(del);
    q->_size--;
}

// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
    assert(q != NULL);
    assert(q->_front != NULL);
    return q->_front->_data;
}

// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
    assert(q != NULL);
    assert(q->_rear != NULL);
    return q->_rear->_data;
}

// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
    assert(q != NULL);
    return q->_size;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
    assert(q != NULL);
    return q->_front == NULL ? 1 : 0;
}

// 销毁队列
void QueueDestroy(Queue* q)
{
    assert(q != NULL);
    QNode* cur = q->_front;
    while (cur != NULL)
    {
        QNode* next = cur->_pNext;
        free(cur);
        cur = next;
    }
    q->_front = NULL;
    q->_rear = NULL;
    q->_size = 0;
}

// 测试入口
int main()
{
    Queue q;
    QueueInit(&q);

    printf("入队 10, 20, 30\n");
    QueuePush(&q, 10);
    QueuePush(&q, 20);
    QueuePush(&q, 30);
    printf("队头: %d, 队尾: %d, 大小: %d\n",
           QueueFront(&q), QueueBack(&q), QueueSize(&q));   // 队头: 10, 队尾: 30, 大小: 3

    printf("出队\n");
    QueuePop(&q);
    printf("队头: %d, 队尾: %d, 大小: %d\n",
           QueueFront(&q), QueueBack(&q), QueueSize(&q));   // 队头: 20, 队尾: 30, 大小: 2

    printf("再入队 40, 50\n");
    QueuePush(&q, 40);
    QueuePush(&q, 50);

    printf("当前队列: ");
    while (!QueueEmpty(&q))
    {
        printf("%d ", QueueFront(&q));
        QueuePop(&q);
    }
    printf("\n");
    printf("队列空: %s\n", QueueEmpty(&q) ? "是" : "否");   // 队列空: 是

    QueueDestroy(&q);
    return 0;
}

队列

队列的概念及结构

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

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

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

队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。

接口

cpp 复制代码
typedef int QDataType;

// 队列结点
typedef struct QListNode
{
    struct QListNode* _pNext;
    QDataType _data;
} QNode;

// 队列结构(含头尾指针和元素个数)
typedef struct Queue
{
    QNode* _front;   // 队头指针
    QNode* _rear;    // 队尾指针
    int _size;       // 队列中元素个数
} Queue;

// 初始化队列
void QueueInit(Queue* q);

// 队尾入队列
void QueuePush(Queue* q, QDataType data);

// 队头出队列
void QueuePop(Queue* q);

// 获取队列头部元素
QDataType QueueFront(Queue* q);

// 获取队列队尾元素
QDataType QueueBack(Queue* q);

// 获取队列中有效元素个数
int QueueSize(Queue* q);

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q);

// 销毁队列
void QueueDestroy(Queue* q);

1. 初始化队列

解释

将队列的头尾指针置为 NULL,元素个数置为 0。

cpp 复制代码
void QueueInit(Queue* q)
{
    assert(q != NULL);
    q->_front = NULL;
    q->_rear = NULL;
    q->_size = 0;
}

2. 队尾入队列

解释

在链表尾部插入新结点。

先动态申请新结点,赋值。

如果队列为空(_front == NULL),则头尾都指向新结点;否则将当前尾结点的 _pNext 指向新结点,再更新 _rear 指向新结点。最后 _size++

cpp 复制代码
void QueuePush(Queue* q, QDataType data)
{
    assert(q != NULL);
    QNode* newNode = (QNode*)malloc(sizeof(QNode));
    if (newNode == NULL)
    {
        perror("malloc fail");
        return;
    }
    newNode->_data = data;
    newNode->_pNext = NULL;

    if (q->_front == NULL)   // 空队列
    {
        q->_front = newNode;
        q->_rear = newNode;
    }
    else
    {
        q->_rear->_pNext = newNode;
        q->_rear = newNode;
    }
    q->_size++;
}

3. 队头出队列

解释

删除队头结点。

需保证队列非空。

保存队头结点,将 _front 指向下一个结点。如果队头成为 NULL(即队列变空),则 _rear 也要置为 NULL。释放原队头结点,_size--

cpp 复制代码
void QueuePop(Queue* q)
{
    assert(q != NULL);
    assert(q->_front != NULL);   // 队列非空
    QNode* del = q->_front;
    q->_front = q->_front->_pNext;
    if (q->_front == NULL)       // 删除后队列为空
        q->_rear = NULL;
    free(del);
    q->_size--;
}

4. 获取队列头部元素

解释

返回队头结点的数据,但不删除。需保证队列非空。

cpp 复制代码
QDataType QueueFront(Queue* q)
{
    assert(q != NULL);
    assert(q->_front != NULL);
    return q->_front->_data;
}

5. 获取队列队尾元素

解释

返回队尾结点的数据。需保证队列非空。

cpp 复制代码
QDataType QueueBack(Queue* q)
{
    assert(q != NULL);
    assert(q->_rear != NULL);
    return q->_rear->_data;
}

6. 获取队列中有效元素个数

解释

直接返回 _size 字段的值。

cpp 复制代码
int QueueSize(Queue* q)
{
    assert(q != NULL);
    return q->_size;
}

7. 检测队列是否为空

解释

如果队列为空返回非零(通常 1),否则返回 0。可以判断 _front == NULL_size == 0

cpp 复制代码
int QueueEmpty(Queue* q)
{
    assert(q != NULL);
    return q->_front == NULL ? 1 : 0;
}

8. 销毁队列

解释

释放链表中所有结点,防止内存泄漏。

遍历链表,依次 free 每个结点,最后将头尾指针置 NULL,大小置 0。

cpp 复制代码
void QueueDestroy(Queue* q)
{
    assert(q != NULL);
    QNode* cur = q->_front;
    while (cur != NULL)
    {
        QNode* next = cur->_pNext;
        free(cur);
        cur = next;
    }
    q->_front = NULL;
    q->_rear = NULL;
    q->_size = 0;
}

代码总

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

typedef int QDataType;

typedef struct QListNode
{
    struct QListNode* _pNext;
    QDataType _data;
} QNode;

typedef struct Queue
{
    QNode* _front;
    QNode* _rear;
    int _size;
} Queue;

void QueueInit(Queue* q);
void QueuePush(Queue* q, QDataType data);
void QueuePop(Queue* q);
QDataType QueueFront(Queue* q);
QDataType QueueBack(Queue* q);
int QueueSize(Queue* q);
int QueueEmpty(Queue* q);
void QueueDestroy(Queue* q);

// 实现部分
int main()
{
    Queue q;
    QueueInit(&q);

    printf("入队 10, 20, 30\n");
    QueuePush(&q, 10);
    QueuePush(&q, 20);
    QueuePush(&q, 30);
    printf("队头: %d, 队尾: %d, 大小: %d\n",
           QueueFront(&q), QueueBack(&q), QueueSize(&q));   // 队头: 10, 队尾: 30, 大小: 3

    printf("出队\n");
    QueuePop(&q);
    printf("队头: %d, 队尾: %d, 大小: %d\n",
           QueueFront(&q), QueueBack(&q), QueueSize(&q));   // 队头: 20, 队尾: 30, 大小: 2

    printf("再入队 40, 50\n");
    QueuePush(&q, 40);
    QueuePush(&q, 50);

    printf("当前队列: ");
    while (!QueueEmpty(&q))
    {
        printf("%d ", QueueFront(&q));
        QueuePop(&q);
    }
    printf("\n"); 
    printf("队列空: %s\n", QueueEmpty(&q) ? "是" : "否");   // 队列空: 是

    QueueDestroy(&q);
    return 0;
}

循环队列

扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。Linux中的生产者消费者模型就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

为什么需要循环队列

普通队列(顺序存储)如果用数组实现,入队时 rear 后移,出队时 front 后移。但经过多次出队后,数组前面会出现无法再利用的空闲位置,造成"假溢出"------数组还有空位,但 rear 已经指向末尾,无法再入队。

循环队列将数组的首尾逻辑上连接起来,当 rear 到达数组末尾时,下一步就回到开头,从而复用空闲空间。

循环队列的结构(数组实现)

一个固定大小的数组 data[0..k-1]

两个整型下标:front 指向队头元素的位置,rear 指向队尾元素的下一个位置(或指向队尾元素,具体取决于实现)

初始状态:front = rear = 0,队列为空

常用约定:

入队:将元素放入 rear 位置,rear = (rear + 1) % capacity

出队:取出 front 位置元素,front = (front + 1) % capacity

区分队空和队满

若使用 front == rear 表示空,那么满时也会出现 front == rear(因为绕一圈回来了),造成冲突。

解决方法(二选一):

方法一:牺牲一个存储单元

约定当 (rear + 1) % capacity == front 时,认为队列已满。

此时数组中还有一个空位,但不使用,用于区分空和满。

队列中最多存放 capacity - 1 个元素。

方法二:增加一个 size 变量

在结构体中增加一个 int size,入队时 size++,出队时 size--

判断空:size == 0;判断满:size == capacity

优点:不浪费空间;缺点:多维护一个变量。

循环队列的基本操作(以牺牲一个单元的方法为例)

数据结构定义

cpp 复制代码
typedef struct {
    int* data;      // 动态数组
    int front;      // 队头下标(指向队头元素)
    int rear;       // 队尾下标(指向队尾元素的下一个位置)
    int capacity;   // 数组总长度(实际容量 = capacity - 1)
} CircularQueue;

1. 初始化循环队列

解释

创建一个大小为 k 的循环队列(用户期望最多存放 k 个元素)。为了区分空和满,实际数组长度需要 k+1

动态分配数组空间,将 frontrear 均初始化为 0。

cpp 复制代码
void cqInit(CircularQueue* q, int k) {
    // 分配 k+1 个整型空间(多一个用于判满)
    q->data = (int*)malloc(sizeof(int) * (k + 1));
    // 初始时队头和队尾重合,队列为空
    q->front = 0;
    q->rear = 0;
    // 记录数组总长度
    q->capacity = k + 1;
}

2. 判断队列是否为空

解释

front == rear 时,队列中没有元素。

cpp 复制代码
int cqIsEmpty(CircularQueue* q) {
    // 队头与队尾重合表示空队列
    return q->front == q->rear;
}

3. 判断队列是否已满

解释

(rear + 1) % capacity == front 时,队列已满。此时如果再入队,会覆盖未出队的元素,因此不允许入队。

cpp 复制代码
int cqIsFull(CircularQueue* q) {
    // 队尾的下一个位置是队头,则队列已满(牺牲一个单元)
    return (q->rear + 1) % q->capacity == q->front;
}

4. 入队

解释

若队列已满则操作失败。

否则将元素放入 data[rear] 位置,然后 rear 后移一位(取模)。

cpp 复制代码
int cqEnQueue(CircularQueue* q, int value) {
    // 先检查队列是否已满
    if (cqIsFull(q))
        return -1;   // 入队失败
    // 将新元素放到当前 rear 位置
    q->data[q->rear] = value;
    // rear 后移一位,如果到末尾则绕回开头(取模)
    q->rear = (q->rear + 1) % q->capacity;
    return 0;        // 入队成功
}

5. 出队

解释

若队列为空则操作失败。

否则取出 data[front] 的值(通过输出参数返回),然后 front 后移一位(取模)。

cpp 复制代码
int cqDeQueue(CircularQueue* q, int* out) {
    // 先检查队列是否为空
    if (cqIsEmpty(q))
        return -1;   // 出队失败
    // 取出队头元素
    *out = q->data[q->front];
    // front 后移一位,取模实现环形
    q->front = (q->front + 1) % q->capacity;
    return 0;        // 出队成功
}

6. 获取队头元素

解释

只读取队头元素的值,不出队。需保证队列非空。

cpp 复制代码
int cqFront(CircularQueue* q, int* out) {
    // 队列为空则无法获取
    if (cqIsEmpty(q))
        return -1;
    // 将队头元素通过输出参数返回
    *out = q->data[q->front];
    return 0;
}

7. 获取队尾元素

解释

队尾元素位于 rear 的前一个位置(因为 rear 指向下一个空位)。需要处理 rear 为 0 时的回绕情况。

cpp 复制代码
int cqRear(CircularQueue* q, int* out) {
    // 队列为空则无法获取
    if (cqIsEmpty(q))
        return -1;
    // 计算队尾下标: (rear - 1 + capacity) % capacity
    int tailIndex = (q->rear - 1 + q->capacity) % q->capacity;
    *out = q->data[tailIndex];
    return 0;
}

8. 获取队列中元素个数

解释

由于队列是环形的,元素个数计算公式为:(rear - front + capacity) % capacity

cpp 复制代码
int cqSize(CircularQueue* q) {
    // 环形队列长度计算公式
    return (q->rear - q->front + q->capacity) % q->capacity;
}

9. 销毁队列

解释

释放动态数组内存,并将指针置空,相关变量归零,防止内存泄漏。

cpp 复制代码
void cqDestroy(CircularQueue* q) {
    // 释放数组内存
    free(q->data);
    // 将指针置空,避免野指针
    q->data = NULL;
    // 重置其他成员
    q->front = 0;
    q->rear = 0;
    q->capacity = 0;
}

代码总

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

// 循环队列结构定义(牺牲一个单元判满)
typedef struct {
    int* data;      // 动态数组
    int front;      // 队头下标(指向队头元素)
    int rear;       // 队尾下标(指向队尾元素的下一个位置)
    int capacity;   // 数组总长度(实际有效容量 = capacity - 1)
} CircularQueue;

// 初始化循环队列
void cqInit(CircularQueue* q, int k) {
    // 分配 k+1 个空间,留一个用于判满
    q->data = (int*)malloc(sizeof(int) * (k + 1));
    q->front = 0;
    q->rear = 0;
    q->capacity = k + 1;
}

// 判断队列是否为空
int cqIsEmpty(CircularQueue* q) {
    return q->front == q->rear;
}

// 判断队列是否已满
int cqIsFull(CircularQueue* q) {
    return (q->rear + 1) % q->capacity == q->front;
}

// 入队
int cqEnQueue(CircularQueue* q, int value) {
    if (cqIsFull(q)) {
        return -1;          // 队列已满,入队失败
    }
    q->data[q->rear] = value;
    q->rear = (q->rear + 1) % q->capacity;
    return 0;
}

// 出队
int cqDeQueue(CircularQueue* q, int* out) {
    if (cqIsEmpty(q)) {
        return -1;          // 队列为空,出队失败
    }
    *out = q->data[q->front];
    q->front = (q->front + 1) % q->capacity;
    return 0;
}

// 获取队头元素(不出队)
int cqFront(CircularQueue* q, int* out) {
    if (cqIsEmpty(q)) {
        return -1;
    }
    *out = q->data[q->front];
    return 0;
}

// 获取队尾元素(不删除)
int cqRear(CircularQueue* q, int* out) {
    if (cqIsEmpty(q)) {
        return -1;
    }
    // 队尾元素在 rear 的前一个位置,处理绕回
    int tailIndex = (q->rear - 1 + q->capacity) % q->capacity;
    *out = q->data[tailIndex];
    return 0;
}

// 返回队列中实际元素个数
int cqSize(CircularQueue* q) {
    return (q->rear - q->front + q->capacity) % q->capacity;
}

// 销毁队列,释放内存
void cqDestroy(CircularQueue* q) {
    free(q->data);
    q->data = NULL;
    q->front = q->rear = q->capacity = 0;
}

// 测试入口
int main() {
    CircularQueue q;
    cqInit(&q, 3);          // 最多存放 3 个元素

    printf("入队 1, 2, 3\n");
    cqEnQueue(&q, 1);
    cqEnQueue(&q, 2);
    cqEnQueue(&q, 3);
    int val;
    cqFront(&q, &val);
    printf("队头: %d, 队尾: %d, 大小: %d\n", val, (cqRear(&q, &val), val), cqSize(&q)); // 队头: 1, 队尾: 3, 大小: 3

    printf("再入队 4(应失败)\n");
    int ret = cqEnQueue(&q, 4);
    printf("入队结果: %d\n", ret); // 入队结果: -1

    printf("出队\n");
    cqDeQueue(&q, &val);
    printf("出队元素: %d\n", val); // 出队元素: 1
    cqFront(&q, &val);
    printf("新队头: %d, 大小: %d\n", val, cqSize(&q)); // 新队头: 2, 大小: 2

    printf("再入队 4\n");
    cqEnQueue(&q, 4);
    cqRear(&q, &val);
    printf("队尾: %d, 大小: %d\n", val, cqSize(&q)); // 队尾: 4, 大小: 3

    printf("队列元素依次出队: ");
    while (!cqIsEmpty(&q)) {
        cqDeQueue(&q, &val);
        printf("%d ", val);
    }
    printf("\n"); // 输出: 2 3 4

    cqDestroy(&q);
    return 0;
}

栈和队列面试题

  1. 括号匹配问题。20. 有效的括号 - 力扣(LeetCode)

  2. 用队列实现栈。225. 用队列实现栈 - 力扣(LeetCode)

  3. 用栈实现队列。232. 用栈实现队列 - 力扣(LeetCode)

  4. 设计循环队列。622. 设计循环队列 - 力扣(LeetCode)

概念选择题

1. 一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是( )。

A 12345ABCDE

B EDCBA54321

C ABCDE12345

D 54321EDCBA

2. 若进栈序列为 1,2,3,4,进栈过程中可以出栈,则下列不可能的一个出栈序列是( )。

A 1,4,3,2

B 2,3,4,1

C 3,1,4,2

D 3,4,2,1

3. 循环队列的存储空间为 Q(1:100),初始状态为 front=rear=100。经过一系列正常的入队与退队操作后,front=rear=99,则循环队列中的元素个数为( )。

A 1

B 2

C 99

D 0 或者 100

4. 以下( )不是队列的基本运算?

A 从队尾插入一个新元素

B 从队列中删除第1个元素

C 判断一个队列是否为空

D 读取队头元素的值

5. 现有一循环队列,其队头指针为 front,队尾指针为 rear;循环队列长度为 N。其队内有效长度为?(假设队头不存放数据)

A (rear - front + N) % N + 1

B (rear - front + N) % N

C (rear - front) % (N + 1)

D (rear - front + N) % (N - 1)

相关推荐
凡人叶枫1 天前
Effective C++ 条款39:明智而审慎地使用 private 继承
java·数据库·c++·嵌入式开发
不想写代码的星星1 天前
伪共享:逻辑无共享,物理打成狗
c++
noipp1 天前
【无标题】
c语言·数据结构·c++·算法
森G1 天前
64、完善聊天室程序(TLV拓展)---------网络编程
网络·c++·tcp/ip
郝学胜-神的一滴1 天前
完全二叉树与堆底层原理深度剖析 | 手写C++大顶堆实现
java·开发语言·数据结构·c++·python·算法
大白话_NOI1 天前
【洛谷 P2678】 [NOIP2015 提高组] 跳石头 超详细题解
c++·算法
悠仁さん1 天前
数据结构 图(代码实现篇 C语言版)
数据结构·算法·图论
chase_my_dream1 天前
LeGO-LOAM 详细源码流程解读
c++·计算机视觉·自动驾驶
插件开发1 天前
vs2015 cuda c++ 线程号的计算详解
开发语言·c++·算法
有点。1 天前
C++(前缀和与差分)
c++·算法