栈与队列OJ题精选,数据结构的实践应用

系列文章目录

🎈 🎈 我的CSDN主页 :OTWOL的主页,欢迎!!!👋🏼👋🏼

🎉🎉我的C语言初阶合集C语言初阶合集,希望能帮到你!!!😍 😍

🔍🔍我的C语言进阶合集我的C语言进阶合集,期待你的点击!!!🌈🌈

🎉🎉我的数据结构与算法合集数据结构与算法合集,点进去看看吧!!! 🎊🎊
😍 👋🏼🎉🎊创作不易,欢迎大家留言、点赞加收藏!!! 🥳😁😍

文章目录

一、有效的括号

(1)题目描述

下面是该题的链接🔗

有效的括号

(2)代码示例

写一个 顺序 栈:

c 复制代码
// 定义一个类型别名,将 char 类型重命名为 STDataType,方便后续代码中使用,提高代码可读性和可维护性
typedef char STDataType;

// 定义一个结构体,用于表示栈结构
// 其中包含一个指向存储栈元素的数组指针 a,栈顶索引 top,以及栈的容量 capacity
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;

// 初始化栈的函数
// 接收一个指向栈结构体的指针 ps,用于对栈进行初始化操作
void STInit(ST* ps)
{
    // 断言传入的指针不为空,若为空则表示出现了错误情况,因为后续要通过该指针操作栈结构体
    assert(ps);

    // 初始设置栈的容量为 4
    ps->capacity = 4;
    // 初始时 栈顶索引 为 0,表示栈为空
    ps->top = 0;

    // 动态分配内存来存储栈元素,根据初始容量分配相应大小的内存空间
    // 将分配的内存地址强转为 STDataType* 类型后赋值给临时指针 tmp
    STDataType* tmp = (STDataType*)malloc(ps->capacity * sizeof(STDataType));
    // 如果内存分配失败(返回的指针为NULL)
    if (tmp == NULL)
    {
        // 输出错误信息,提示malloc分配内存失败
        perror("malloc fail");
        // 直接返回,不再进行后续初始化操作
        return;
    }
    // 将成功分配内存的地址赋值给栈结构体中的数组指针 a,使得栈可以使用这块内存来存储元素
    ps->a = tmp;
}

// 销毁栈的函数,用于释放栈所占用的内存资源等清理工作
void STDestroy(ST* ps)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(ps);

    // 释放栈中存储元素的数组所占用的内存空间
    free(ps->a);
    // 将栈结构体中的数组指针 a 置为 NULL,防止出现野指针情况
    ps->a = NULL;
    // 将 栈顶索引 和 容量 都重置为 0,恢复到初始的 "空" 状态表示
    ps->top = ps->capacity = 0;
}

// 向栈中压入元素的函数
// 接收一个指向栈结构体的指针 ps 以及要压入的元素 x(类型为 STDataType)
void STPush(ST* ps, STDataType x)
{
    // 断言传入的指针不为空,保证后续操作能正常进行
    assert(ps);
    // 检查栈是否已满(即当前元素个数等于栈的容量),如果已满则需要进行扩容操作
    if (ps->capacity == ps->top)
    {
        // 使用 realloc 对栈的存储空间进行扩容,扩容为原来的 2 倍大小
        // 将重新分配后的内存地址强转为STDataType*类型后赋值给临时指针 tmp
        STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
        // 如果扩容失败(返回的指针为 NULL)
        if (tmp == NULL)
        {
            // 输出错误信息,提示 realloc 扩容失败
            perror("realloc fail");
            // 直接返回,不进行元素压入操作了
            return;
        }
        // 将扩容后成功分配的内存地址赋值给栈结构体中的数组指针 a,更新栈的存储空间
        ps->a = tmp;
        // 将栈的容量更新为原来的 2 倍
        ps->capacity *= 2;
    }
    // 将元素 x 存放到栈顶位置,然后栈顶索引 top 自增 1,指向下一个空闲位置
    ps->a[ps->top++] = x;
}

// 判断栈是否为空的函数
// 接收一个指向栈结构体的指针 ps
bool STEmpty(ST* ps)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(ps);

    // 通过判断 栈顶索引 是否为 0 来确定栈是否为空,返回相应的布尔值
    return (ps->top == 0);
}

// 获取栈中元素个数的函数
// 接收一个指向栈结构体的指针 ps
int STSize(ST* ps)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(ps);

    // 直接返回 栈顶索引 的值,因为 栈顶索引 的值就代表了当前栈中元素的个数
    return (ps->top);
}

// 弹出栈顶元素的函数(只是将 栈顶索引 减 1,逻辑上表示栈顶元素被移除了)
// 接收一个指向栈结构体的指针 ps
void STPop(ST* ps)
{
    // 断言传入的指针不为空,保证操作的合法性
    assert(ps);
    // 断言栈不能为空,若为空则不能进行弹出操作,否则会出现错误情况
    assert(!STEmpty(ps));

    // 将 栈顶索引 减 1,实现逻辑上的弹出栈顶元素操作
    --ps->top;
}

// 获取栈顶元素的函数(但不会弹出栈顶元素)
// 接收一个指向栈结构体的指针 ps
STDataType STTop(ST* ps)
{
    // 断言传入的指针不为空,保证操作的合法性
    assert(ps);
    // 断言栈不能为空,若为空则没有栈顶元素可供获取,会出现错误情况
    assert(!STEmpty(ps));

    // 返回栈顶元素的值(通过 栈顶索引 减 1 的索引来获取,因为数组下标从 0 开始)
    return (ps->a[ps->top - 1]);
}

判断有效括号

c 复制代码
// 判断给定的括号字符串s是否有效(左右括号匹配)的函数
bool isValid(char* s) 
{
    // 创建一个栈结构体实例 st
    ST st;
    // 初始化栈st
    STInit(&st);
    // 遍历字符串s,直到遇到字符串结束符'\0'
    while(*s)
    {
        // 如果当前字符是左括号('(' 或 '[' 或 '{')
        if(*s == '(' || *s == '[' || *s == '{')
        {
            // 将该左括号压入栈st中
            STPush(&st,*s);
        }
        else
        {
            // 如果栈为空,说明没有与之匹配的左括号了,括号字符串无效
            if(STEmpty(&st))
            {
                // 销毁栈,释放资源
                STDestroy(&st);
                return false;
            }
            // 如果栈不为空,说明有对应的左括号
            else
            {
                // 获取栈顶元素
                STDataType top = STTop(&st);
                // 弹出栈顶元素(因为已经获取到了,并且后续要检查匹配情况,所以可以弹出)
                STPop(&st);

                // 检查当前右括号与获取到的栈顶左括号是否匹配,如果不匹配则括号字符串无效
                if(     *s == ')' && top!= '('
                    || *s == ']' && top!= '['
                    || *s == '}' && top!= '{')
                {
                    // 销毁栈,释放资源
                    STDestroy(&st);
                    return false;
                }
            }
        }
        // 移动指针到下一个字符继续检查
        ++s;
    }
    // 遍历完字符串后,检查栈是否为空,如果为空则说明所有括号都匹配,返回true,否则返回false
    bool ret = STEmpty(&st);
    // 销毁栈,释放资源
    STDestroy(&st);
    
    return ret;
}

二、用队列实现栈

(1)题目描述

下面是该题的链接🔗

用队列实现栈

(2)代码示例

写一个 链式 队列:

c 复制代码
// 定义一个类型别名,将 int 类型重命名为 QDataType,方便后续代码中使用,增强代码可读性和可维护性
typedef int QDataType;

// 定义结构体 QNode,表示队列中的节点
// 其中包含一个数据域 data,用于存储节点的数据(类型为 QDataType,即int)
// 以及一个指针域 next,用于指向下一个节点,从而构成链式结构
typedef struct QNode
{
    QDataType data;
    struct QNode* next;
}QNode;

// 定义结构体 Queue,用于表示队列
// 包含指向队头节点的指针 head、指向队尾节点的指针 tail,以及记录队列中元素个数的 size
typedef struct Queue
{
    QNode* head;
    QNode* tail;
    int size;
}Queue;

// 初始化队列的函数
// 接收一个指向 Queue 结构体的指针 q,用于对队列进行初始化操作
void QInit(Queue* q)
{
    // 断言传入的指针不为空,若为空则表示出现了错误情况,因为后续要通过该指针操作队列结构体
    assert(q);

    // 初始化时,队头和队尾指针都设为 NULL,表示队列为空
    q->head = q->tail = NULL;
    // 队列中元素个数初始化为 0
    q->size = 0;
}

// 销毁队列的函数,用于释放队列及其节点所占用的内存资源等清理工作
void QDestroy(Queue* q)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(q);

    // 从队头开始遍历队列的每个节点
    QNode* cur = q->head;
    while (cur)
    {
        // 保存当前节点的下一个节点指针,方便后续释放当前节点内存后能继续遍历下一个节点
        QNode* next = cur->next;
        // 释放当前节点所占用的内存空间
        free(cur);
        // 将指针更新为下一个节点的指针,继续循环释放下一个节点内存
        cur = next;
    }
    // 释放完所有节点后,将队头和队尾指针都置为 NULL,表示队列已被销毁
    q->head = q->tail = NULL;
    // 队列元素个数也重置为 0
    q->size = 0;
}

// 判断队列是否为空的函数
// 接收一个指向 Queue 结构体的指针 q
bool QEmpty(Queue* q)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(q);

    // 通过判断队列元素个数是否为 0 来确定队列是否为空,返回相应的布尔值
    return (q->size == 0);
}

// 获取队列中元素个数的函数
// 接收一个指向 Queue 结构体的指针 q
int QSize(Queue* q)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(q);

    // 直接返回队列中元素个数 size 的值
    return (q->size);
}

// 向队列中插入元素的函数(队尾插入操作)
// 接收一个指向 Queue 结构体的指针 q 以及要插入的元素 x(类型为 QDataType,即 int)
void QPush(Queue* q, QDataType x)
{
    // 断言传入的指针不为空,保证后续操作能正常进行
    assert(q);

    // 申请一个新的队列节点空间
    QNode* newnode = (QNode*)malloc(sizeof(QNode));
    // 如果内存分配失败(返回的指针为 NULL)
    if (newnode == NULL)
    {
        // 输出错误信息,提示 malloc 分配内存失败
        perror("malloc fail");
        // 直接返回,不再进行元素插入操作
        return;
    }
    // 给新节点的数据域赋值为要插入的元素 x
    newnode->data = x;
    // 新节点的 next 指针初始化为 NULL,因为它目前是队尾节点,后面没有其他节点
    newnode->next = NULL;

    // 如果队列当前为空(队头指针为 NULL)
    if (q->head == NULL)
    {
        // 断言队尾指针也必然为 NULL,确保队列状态的一致性
        assert(q->tail == NULL);
        // 将队头和队尾指针都指向新创建的节点,因为此时队列中只有这一个节点
        q->head = q->tail = newnode;
    }
    else
    {
        // 将当前队尾节点的 next 指针指向新节点,即将新节点连接到队列末尾
        q->tail->next = newnode;
        // 更新队尾指针为新插入的节点,使其始终指向队尾
        q->tail = q->tail->next;
    }
    // 队列元素个数自增 1,表示插入了一个新元素
    ++q->size;
}

// 从队列中弹出元素的函数(队头删除操作)
// 接收一个指向 Queue 结构体的指针 q
void QPop(Queue* q)
{
    // 断言传入的指针不为空,保证操作的合法性
    assert(q);
    // 断言队列不能为空,若为空则不能进行弹出操作,否则会出现错误情况
    assert(!QEmpty(q));
    // 进行队头删除操作

    // 如果队列中只有一个元素(元素个数为 1)
    if (q->size == 1)
    {
        // 释放队头节点所占用的内存空间
        free(q->head);
        // 将队头和队尾指针都置为 NULL,因为此时队列已为空
        q->head = q->tail = NULL;
    }
    else
    {
        // 保存队头节点的下一个节点指针,方便后续更新队头指针
        QNode* next = q->head->next;
        // 释放当前队头节点所占用的内存空间
        free(q->head);
        // 更新队头指针为之前保存的下一个节点指针,实现队头节点的删除
        q->head = next;
    }
    // 队列元素个数自减 1,表示弹出了一个元素
    --q->size;
}

// 获取队列队头元素的函数
// 接收一个指向 Queue 结构体的指针 q
QDataType QFront(Queue* q)
{
    // 断言传入的指针不为空,保证操作的合法性
    assert(q);
    // 断言队列不能为空,若为空则没有队头元素可供获取,会出现错误情况
    assert(!QEmpty(q));

    // 返回队头节点的数据域的值,即获取队头元素
    return (q->head->data);
}

// 获取队列队尾元素的函数(但不会删除队尾元素)
// 接收一个指向 Queue 结构体的指针 q
QDataType QBack(Queue* q)
{
    // 断言传入的指针不为空,保证操作的合法性
    assert(q);
    // 断言队列不能为空,若为空则没有队尾元素可供获取,会出现错误情况
    assert(!QEmpty(q));

    // 返回队尾节点的数据域的值,即获取队尾元素
    return (q->tail->data);
}

用队列实现栈

c 复制代码
// 定义一个结构体 MyStack,它内部包含两个 Queue 类型的成员 q1 和 q2
// 这里是通过组合两个队列来模拟实现栈的功能
typedef struct 
{
    Queue q1;
    Queue q2;
} MyStack;

// 创建一个 MyStack 结构体实例的函数,并进行初始化操作
// 返回指向新创建的 MyStack 结构体的指针
MyStack* myStackCreate() 
{
    // 动态分配内存来存储一个 MyStack 结构体大小的空间
    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    // 如果内存分配失败(返回的指针为 NULL)
    if(obj == NULL)
    {
        // 输出错误信息,提示 malloc 分配内存失败
        perror("malloc fail");
        // 返回 NULL,表示创建失败
        return NULL;
    }

    // 初始化 MyStack 结构体中的 q1 队列
    QInit(&(obj->q1));
    // 初始化 MyStack 结构体中的 q2 队列
    QInit(&(obj->q2));

    // 返回指向初始化好的 MyStack 结构体的指针
    return obj;
}

// 向模拟栈( MyStack 结构体表示)中压入元素的函数
// 参数 obj 是指向 MyStack 结构体的指针,x 是要压入的元素(int 类型)
void myStackPush(MyStack* obj, int x) 
{
    // 如果 q1 队列不为空
    if(!QEmpty(&(obj->q1)))
    {
        // 将元素 x 压入q1队列
        QPush(&(obj->q1),x);
    }
    else
    {
        // 否则将元素 x 压入 q2 队列
        QPush(&(obj->q2),x);
    }
}

// 从模拟栈( MyStack 结构体表示)中弹出元素的函数
// 参数 obj 是指向 MyStack 结构体的指针,返回弹出的栈顶元素( int 类型)
int myStackPop(MyStack* obj) 
{
    // 先假设 q1 队列为空队列,q2 队列为非空队列(后续会根据实际情况调整)
    Queue* emptyq = &(obj->q1);
    Queue* noemptyq = &(obj->q2);
    // 如果实际情况是 q1 队列不为空
    if(!QEmpty(&(obj->q1)))
    {
        // 则交换假设,让 q2 队列为空队列,q1 队列为非空队列
        emptyq = &(obj->q2);
        noemptyq = &(obj->q1); 
    }
    // 将非空队列(除了最后一个元素外)中的元素依次转移到空队列中
    while(QSize(noemptyq) > 1)
    {
        // 取出 非空队列 的队头元素,并将其压入 空队列
        QPush(emptyq,QFront(noemptyq));
        // 弹出非空队列的队头元素(完成转移操作)
        QPop(noemptyq);
    }
    // 获取非空队列此时剩下的最后一个元素(即 栈顶 元素)
    int top = QFront(noemptyq);
    // 弹出这个最后元素,完成 出栈 操作
    QPop(noemptyq);
    
    // 返回弹出的 栈顶元素
    return top;
}

// 获取模拟栈( MyStack 结构体表示)的栈顶元素的函数(但不弹出元素)
// 参数 obj 是指向 MyStack 结构体的指针,返回栈顶元素( int 类型)
int myStackTop(MyStack* obj) 
{
    // 如果 q1 队列不为空
    if(!QEmpty(&(obj->q1)))
    {
        // 返回 q1 队列的队尾元素作为栈顶元素(因为模拟栈的栈顶元素相当于非空队列的队尾元素)
        return QBack(&(obj->q1));
    }    
    else
    {
        // 否则返回 q2 队列的队尾元素作为栈顶元素
        return QBack(&(obj->q2));
    }
}

// 判断模拟栈( MyStack 结构体表示)是否为空的函数
// 参数 obj 是指向 MyStack 结构体的指针,返回布尔值表示栈是否为空
bool myStackEmpty(MyStack* obj) 
{
    // 当 q1 队列和 q2 队列都为空时,模拟栈为空,返回 true,否则返回false
    return QEmpty(&(obj->q1)) && QEmpty(&(obj->q2));    
}

// 释放模拟栈( MyStack 结构体表示)所占用的内存资源的函数
// 参数 obj 是指向 MyStack 结构体的指针
void myStackFree(MyStack* obj) 
{
    // 先销毁 MyStack 结构体中的 q1 队列,释放其占用的内存
    QDestroy(&(obj->q1));
    // 再销毁 MyStack 结构体中的 q2 队列,释放其占用的内存
    QDestroy(&(obj->q2));
    // 释放 MyStack 结构体本身所占用的内存空间
    free(obj);
}

三、用栈实现队列

(1)题目描述

下面是该题的链接🔗

用栈实现队列

(2)代码示例

写一个 顺序 栈:

c 复制代码
// 定义一个类型别名,将 char 类型重命名为 STDataType,方便后续代码中使用,提高代码可读性和可维护性
typedef char STDataType;

// 定义一个结构体,用于表示栈结构
// 其中包含一个指向存储栈元素的数组指针 a,栈顶索引 top,以及栈的容量 capacity
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;

// 初始化栈的函数
// 接收一个指向栈结构体的指针 ps,用于对栈进行初始化操作
void STInit(ST* ps)
{
    // 断言传入的指针不为空,若为空则表示出现了错误情况,因为后续要通过该指针操作栈结构体
    assert(ps);

    // 初始设置栈的容量为 4
    ps->capacity = 4;
    // 初始时 栈顶索引 为 0,表示栈为空
    ps->top = 0;

    // 动态分配内存来存储栈元素,根据初始容量分配相应大小的内存空间
    // 将分配的内存地址强转为 STDataType* 类型后赋值给临时指针 tmp
    STDataType* tmp = (STDataType*)malloc(ps->capacity * sizeof(STDataType));
    // 如果内存分配失败(返回的指针为NULL)
    if (tmp == NULL)
    {
        // 输出错误信息,提示malloc分配内存失败
        perror("malloc fail");
        // 直接返回,不再进行后续初始化操作
        return;
    }
    // 将成功分配内存的地址赋值给栈结构体中的数组指针 a,使得栈可以使用这块内存来存储元素
    ps->a = tmp;
}

// 销毁栈的函数,用于释放栈所占用的内存资源等清理工作
void STDestroy(ST* ps)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(ps);

    // 释放栈中存储元素的数组所占用的内存空间
    free(ps->a);
    // 将栈结构体中的数组指针 a 置为 NULL,防止出现野指针情况
    ps->a = NULL;
    // 将 栈顶索引 和 容量 都重置为 0,恢复到初始的 "空" 状态表示
    ps->top = ps->capacity = 0;
}

// 向栈中压入元素的函数
// 接收一个指向栈结构体的指针 ps 以及要压入的元素 x(类型为 STDataType)
void STPush(ST* ps, STDataType x)
{
    // 断言传入的指针不为空,保证后续操作能正常进行
    assert(ps);
    // 检查栈是否已满(即当前元素个数等于栈的容量),如果已满则需要进行扩容操作
    if (ps->capacity == ps->top)
    {
        // 使用 realloc 对栈的存储空间进行扩容,扩容为原来的 2 倍大小
        // 将重新分配后的内存地址强转为STDataType*类型后赋值给临时指针 tmp
        STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
        // 如果扩容失败(返回的指针为 NULL)
        if (tmp == NULL)
        {
            // 输出错误信息,提示 realloc 扩容失败
            perror("realloc fail");
            // 直接返回,不进行元素压入操作了
            return;
        }
        // 将扩容后成功分配的内存地址赋值给栈结构体中的数组指针 a,更新栈的存储空间
        ps->a = tmp;
        // 将栈的容量更新为原来的 2 倍
        ps->capacity *= 2;
    }
    // 将元素 x 存放到栈顶位置,然后栈顶索引 top 自增 1,指向下一个空闲位置
    ps->a[ps->top++] = x;
}

// 判断栈是否为空的函数
// 接收一个指向栈结构体的指针 ps
bool STEmpty(ST* ps)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(ps);

    // 通过判断 栈顶索引 是否为 0 来确定栈是否为空,返回相应的布尔值
    return (ps->top == 0);
}

// 获取栈中元素个数的函数
// 接收一个指向栈结构体的指针 ps
int STSize(ST* ps)
{
    // 断言传入的指针不为空,确保操作的合法性
    assert(ps);

    // 直接返回 栈顶索引 的值,因为 栈顶索引 的值就代表了当前栈中元素的个数
    return (ps->top);
}

// 弹出栈顶元素的函数(只是将 栈顶索引 减 1,逻辑上表示栈顶元素被移除了)
// 接收一个指向栈结构体的指针 ps
void STPop(ST* ps)
{
    // 断言传入的指针不为空,保证操作的合法性
    assert(ps);
    // 断言栈不能为空,若为空则不能进行弹出操作,否则会出现错误情况
    assert(!STEmpty(ps));

    // 将 栈顶索引 减 1,实现逻辑上的弹出栈顶元素操作
    --ps->top;
}

// 获取栈顶元素的函数(但不会弹出栈顶元素)
// 接收一个指向栈结构体的指针 ps
STDataType STTop(ST* ps)
{
    // 断言传入的指针不为空,保证操作的合法性
    assert(ps);
    // 断言栈不能为空,若为空则没有栈顶元素可供获取,会出现错误情况
    assert(!STEmpty(ps));

    // 返回栈顶元素的值(通过 栈顶索引 减 1 的索引来获取,因为数组下标从 0 开始)
    return (ps->a[ps->top - 1]);
}

用栈实现队列:

c 复制代码
// 定义一个结构体 MyQueue,它内部包含两个 ST 类型的结构体成员 pushst 和 popst
// 这里通过两个栈来模拟实现队列的功能
typedef struct 
{
    ST pushst;
    ST popst;
} MyQueue;

// 创建一个 MyQueue 结构体实例的函数,并进行初始化操作
// 返回指向新创建的 MyQueue 结构体的指针
MyQueue* myQueueCreate() 
{
    // 动态分配内存来存储一个 MyQueue 结构体大小的空间
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    // 如果内存分配失败(返回的指针为 NULL)
    if(obj == NULL)
    {
        // 输出错误信息,提示 malloc 分配内存失败
        perror("malloc fail");
        // 返回 NULL,表示创建失败
        return NULL;
    }

    // 初始化 MyQueue 结构体中的 pushst 栈
    STInit(&(obj->pushst));
    // 初始化 MyQueue 结构体中的 popst 栈
    STInit(&(obj->popst));

    // 返回指向初始化好的 MyQueue 结构体的指针
    return obj;
}

// 向模拟队列( MyQueue 结构体表示)中插入元素的函数
// 参数 obj 是指向 MyQueue 结构体的指针,x 是要插入的元素( int 类型)
void myQueuePush(MyQueue* obj, int x) 
{
    // 调用栈的入栈函数,将元素 x 压入 pushst 栈,模拟队列的入队操作
    STPush(&(obj->pushst),x);
}

// 获取模拟队列( MyQueue 结构体表示)队头元素的函数
// 参数 obj 是指向 MyQueue 结构体的指针,返回队头元素( int 类型)
int myQueuePeek(MyQueue* obj) 
{
    // 如果 popst 栈为空
    if(STEmpty(&(obj->popst)))
    {
        // 当 pushst 栈不为空时,将 pushst 栈中的元素依次弹出并压入popst 栈
        while(!STEmpty(&(obj->pushst)))
        {
            // 取出 pushst 栈的栈顶元素
            STDataType top = STTop(&(obj->pushst));
            // 将该元素压入 popst 栈
            STPush(&(obj->popst), top);
            // 弹出 pushst 栈的栈顶元素
            STPop(&(obj->pushst));
        }
    }
    // 获取 popst 栈的栈顶元素,即为模拟队列的队头元素
    int front = STTop(&(obj->popst)); 
    
    // 返回队头元素
    return front;
}

// 从模拟队列( MyQueue 结构体表示)中删除队头元素的函数
// 参数 obj 是指向 MyQueue 结构体的指针,返回被删除的队头元素( int 类型)
int myQueuePop(MyQueue* obj) 
{
    // 先调用 myQueuePeek 函数获取队头元素(同时也会在 popst 栈为空时进行元素转移操作)
    int front = myQueuePeek(obj);
    // 弹出 popst 栈的栈顶元素,模拟队列的出队操作
    STPop(&(obj->popst));
    
    // 返回被删除的队头元素
    return front;
}

// 判断模拟队列(MyQueue结构体表示)是否为空的函数
// 参数 obj 是指向 MyQueue 结构体的指针,返回布尔值表示队列是否为空
bool myQueueEmpty(MyQueue* obj) 
{
    // 当 pushst 栈和 popst 栈都为空时,模拟队列为空,返回 true,否则返回 false
    return STEmpty(&(obj->pushst)) && STEmpty(&(obj->popst));    
}

// 释放模拟队列( MyQueue 结构体表示)所占用的内存资源的函数
// 参数 obj 是指向 MyQueue 结构体的指针
void myQueueFree(MyQueue* obj) 
{
    // 销毁 MyQueue 结构体中的 pushst 栈,释放其占用的内存
    STDestroy(&(obj->pushst));
    // 销毁 MyQueue 结构体中的 popst 栈,释放其占用的内存
    STDestroy(&(obj->popst));    
    // 释放 MyQueue 结构体本身所占用的内存空间
    free(obj);
}

四、设计循环队列

(1)题目描述

下面是该题的链接🔗

设计循环队列

(2)代码示例

c 复制代码
// 定义一个循环队列的结构体
typedef struct 
{
    int k;          // 队列的容量(最大元素个数)
    int* a;         // 指向队列数组的指针
    int head;       // 队列的头部索引
    int tail;       // 队列的尾部索引
} MyCircularQueue;

// 创建一个循环队列的函数
MyCircularQueue* myCircularQueueCreate(int k) 
{
    // 分配内存空间给队列结构体
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(obj == NULL)
    {
        perror("malloc fail");  // 如果分配失败,输出错误信息
        return NULL;
    }

    // 分配内存空间给队列数组,容量为 k+1(多一个空位用于区分空和满)
    int* tmp = (int*)malloc((k+1) * sizeof(int));
    if(tmp == NULL)
    {
        perror("malloc fail");  // 如果分配失败,输出错误信息
        return NULL;
    }
    obj->a = tmp;  // 将数组指针赋值给结构体的a成员
    obj->k = k;    // 设置队列的容量
    obj->head = obj->tail = 0;  // 初始化头尾索引为 0
    
    return obj;  // 返回创建的队列对象
}

// 判断队列是否为空的函数
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return (obj->head == obj->tail);  // 如果头尾索引相同,则队列为空
}

// 判断队列是否已满的函数
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    // 如果尾部索引加1后模(k+1)等于头部索引,则队列已满
    return ((obj->tail + 1) % (obj->k + 1) == obj->head);    
}

// 向队列中插入元素的函数
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))  // 如果队列已满
        return false;
    else
    {
        obj->a[obj->tail++] = value;  // 将元素插入尾部,并将尾部索引加 1
        obj->tail %= (obj->k + 1);    // 尾部索引模(k+1)以循环
        return true;
    }
}

// 从队列中删除元素的函数
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))  // 如果队列为空
        return false;
    else
    {
        ++obj->head;  // 将头部索引加 1
        obj->head %= (obj->k + 1);    // 头部索引模(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;
    else
    {
        // 计算尾部元素的索引,考虑循环的情况
        return (obj->a[(obj->tail-1 + obj->k + 1) % (obj->k + 1)]);
    }
}

// 释放队列内存的函数
void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->a);  // 释放队列数组的内存
    free(obj);     // 释放队列结构体的内存    
}

END

每天都在学习的路上!
On The Way Of Learning

相关推荐
BingLin-Liu3 小时前
蓝桥杯备考:数据结构之栈 和 stack
数据结构
范纹杉想快点毕业4 小时前
XML通过HTTP POST 请求发送到指定的 API 地址,进行数据回传
xml·c语言·开发语言·数据结构·c++·python·c#
星迹日4 小时前
数据结构:LinkedList与链表—无头单向链表(一)
java·数据结构·经验分享·笔记·链表·单向链表
bachelores4 小时前
数据结构-排序
数据结构·算法·排序算法
tan180°5 小时前
Cpp::C++11右值引用与移动构造(30)
开发语言·数据结构·c++·后端·算法
2401_858286115 小时前
123.【C语言】数据结构之快速排序挖坑法和前后指针法
c语言·开发语言·数据结构·算法·排序算法
羊小猪~~6 小时前
C/C++语言基础--C++STL库算法记录(质变算法、非质变算法、查找、排序、排列组合、关系算法、集合算法、堆算法等)
c语言·开发语言·数据结构·c++·算法·stl
bachelores8 小时前
数据结构-栈、队列和数组
数据结构·算法
好记性+烂笔头8 小时前
hot100_73. 矩阵置零
数据结构·算法·矩阵