栈实现队列

发布于:2025-04-17 ⋅ 阅读:(82) ⋅ 点赞:(0)

用栈实现队列:C 语言代码解析

在数据结构的世界里,栈和队列是两种基础且重要的线性结构。栈遵循后进先出(LIFO)的原则,而队列遵循先进先出(FIFO)的原则。在实现的过程之中,需要两个队列,一个用于出栈,一个用于入栈。

栈的基本实现

首先,我们来看代码中栈的实现部分。

typedef int STDataType;
typedef struct Stack
{
    STDataType* arr;
    int top;     //指向栈顶的位置
    int capacity;//栈的容量
}ST;

这里定义了一个Stack结构体,包含一个动态数组arr用于存储栈中的元素,top表示栈顶元素的位置,capacity表示栈的当前容量。

栈的初始化

void StackInit(ST* ps)
{
    ps->arr = NULL;
    ps->top = ps->capacity = 0;
}

StackInit函数用于初始化栈,将arr指针设为NULL,top和capacity都初始化为 0。

栈的销毁

void StackDestroy(ST* ps)
{
    if (ps->arr)
        free(ps->arr);
    ps->arr = NULL;
    ps->top = ps->capacity = 0;
}

StackDestroy函数负责释放栈所占用的内存空间。如果arr不为空,就释放arr指向的内存,然后将arr设为NULL,并重置top和capacity。

入栈操作

void StackPush(ST* ps, STDataType x)
{
    assert(ps);
    if (ps->top == ps->capacity)
    {
        //增容
        int newCapacity = ps->capacity == 0? 4 : 2 * ps->capacity;
        STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
        if (tmp == NULL)
        {
            perror("realloc fail!");
            exit(1);
        }
        ps->arr = tmp;
        ps->capacity = newCapacity;
    }
    ps->arr[ps->top++] = x;
}

StackPush函数用于将元素x压入栈中。首先检查栈是否已满,如果已满则进行增容操作。增容时,新容量为当前容量为 0 时设为 4,否则为当前容量的 2 倍。使用realloc函数重新分配内存,如果分配失败则打印错误信息并退出程序。最后将元素x放入栈顶位置并将top指针后移。

检查栈是否为空

bool StackEmpty(ST* ps)
{
    assert(ps);
    return ps->top == 0;
}

StackEmpty函数通过检查top是否为 0 来判断栈是否为空。

出栈操作

void StackPop(ST* ps)
{
    assert(!StackEmpty(ps));
    --ps->top;
}

StackPop函数用于弹出栈顶元素,首先确保栈不为空,然后将top指针前移一位。

获取栈顶元素

STDataType StackTop(ST* ps)
{
    assert(!StackEmpty(ps));
    return ps->arr[ps->top - 1];
}

StackTop函数返回栈顶元素,前提是栈不为空。

获取栈中元素个数

i

nt StackSize(ST* ps)
{
    return ps->top;
}

StackSize函数返回栈中当前有效元素的个数,即top的值。

用栈实现队列

接下来,我们看如何利用上述实现的栈来模拟队列的行为。

typedef struct {
    ST push;
    ST pop;
} MyQueue;

这里定义了一个MyQueue结构体,包含两个Stack类型的成员push和pop,分别用于入队和出队操作。

队列的创建

MyQueue* myQueueCreate() {
    MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&pq->push);
    StackInit(&pq->pop);
    return pq;
}

myQueueCreate函数用于创建一个新的队列。首先分配MyQueue结构体大小的内存,然后分别初始化push栈和pop栈。

入队操作

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

myQueuePush函数将元素x入队,实现很简单,直接将元素压入push栈中。

出队操作

int myQueuePop(MyQueue* obj) {
    if(StackEmpty(&obj->pop))
    {
        while(!StackEmpty(&obj->push))
        {int tmp = StackTop(&obj->push);
        StackPush(&obj->pop,tmp);
        StackPop(&obj->push);}
    }

    int top = StackTop(&obj->pop);
    StackPop(&obj->pop);
    return top;
}

myQueuePop函数用于出队操作。首先检查pop栈是否为空,如果为空,则将push栈中的元素依次弹出并压入pop栈中,这样pop栈中的元素顺序就与队列的出队顺序一致了。然后弹出pop栈的栈顶元素并返回。

获取队首元素

int myQueuePeek(MyQueue* obj) {
    if(StackEmpty(&obj->pop))
    {
        while(!StackEmpty(&obj->push))
        {int tmp = StackTop(&obj->push);
        StackPush(&obj->pop,tmp);
        StackPop(&obj->push);}
    }
    return StackTop(&obj->pop);
}

myQueuePeek函数用于获取队首元素。同样,先检查pop栈是否为空,如果为空则将push栈元素转移到pop栈,然后返回pop栈的栈顶元素。

检查队列是否为空

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->push) && StackEmpty(&obj->pop);
}

myQueueEmpty函数通过检查push栈和pop栈是否都为空来判断队列是否为空。

队列的销毁

void myQueueFree(MyQueue* obj) {
    StackDestroy(&obj->push);
    StackDestroy(&obj->pop);
    free(obj);
    obj = NULL;
}

myQueueFree函数用于销毁队列。先分别销毁push栈和pop栈,然后释放MyQueue结构体所占用的内存。

总结

通过上述代码,我们成功地用栈实现了队列的基本操作。这种实现方式巧妙地利用了栈的后进先出特性,通过两个栈的配合来模拟队列的先进先出行为。