栈和队列oj题——225. 用队列实现栈

**
个人主页: 晓风飞
专栏: 数据结构| Linux|| C语言
路漫漫其修远兮,吾将上下而求索


文章目录


要做题目的点击这里-->栈和队列oj题------225. 用队列实现栈

题目要求:

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

void push(int x) 将元素 x 压入栈顶。

int pop() 移除并返回栈顶元素。

int top() 返回栈顶元素。

boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

你只能使用队列的基本操作 ------ 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。

你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:

["MyStack", "push", "push", "top", "pop", "empty"]

[[], [1], [2], [], [], []]

输出:

[null, null, null, 2, 2, false]

解释:

MyStack myStack = new MyStack();

myStack.push(1);

myStack.push(2);

myStack.top(); // 返回 2

myStack.pop(); // 返回 2

myStack.empty(); // 返回 False

提示:

1 <= x <= 9

最多调用100 次 push、pop、top 和 empty

每次调用 pop 和 top 都保证栈不为空
进阶:你能否仅用一个队列来实现栈。

myStackCreate - 创建栈

解题核心

这个问题的核心思路在于使用两个队列(Queue)来模拟一个栈(Stack)的行为。栈是一种后进先出(LIFO, Last In First Out)的数据结构,而队列是一种先进先出(FIFO, First In First Out)的数据结构。要用队列模拟栈的行为,关键在于如何实现栈的两个主要操作:入栈(push)和出栈(pop)。

数据结构的定义

初始化两个队列,这两个队列将用于模拟栈的行为。

c 复制代码
// 定义一个使用两个队列模拟的栈的结构体
typedef struct
{
    Queue q1; // 第一个队列
    Queue q2; // 第二个队列
} MyStack;

初始化栈

动态分配内存给新的栈,如果内存分配失败,输出错误信息并退出。

c 复制代码
// 创建一个新的栈
MyStack* myStackCreate() 
{
    // 动态分配内存给新栈
    MyStack* newStack =(MyStack*)malloc(sizeof(MyStack));
    if (!newStack)
    {
        perror("malloc fail"); // 如果内存分配失败,输出错误信息并退出
        exit(-1);
    }

    // 初始化两个队列
    QInit(&(newStack->q1));
    QInit(&(newStack->q2));
    return newStack;  
}

入栈(Push)操作

在栈中,最新添加的元素总是被存储在栈的顶部。在使用两个队列模拟栈时,入栈操作相对直接:

选择一个非空队列进行操作:如果两个队列都是空的,可以选择任意一个队列进行操作。如果有一个非空队列,总是将新元素入队到这个非空队列。

c 复制代码
// 将一个元素推入栈中
void myStackPush(MyStack* obj, int x) 
{
    assert(obj); // 确保栈对象非空
    // 总是将元素推入非空队列中
    if(!QueueEmpty(&obj->q1))
    {
        QPush(&obj->q1, x);
    }
    else
    {
        QPush(&obj->q2, x);
    }
}

出栈(Pop)操作

确定非空队列和空队列:首先识别出哪个队列是非空的(存有栈元素的队列),哪个队列是空的。

c 复制代码
// 从栈中弹出一个元素
int myStackPop(MyStack* obj) 
{ 
    assert(obj); // 确保栈对象非空
    Queue* empty = &obj->q1; // 一个指向可能为空的队列的指针
    Queue* noEmpty = &obj->q2; // 一个指向非空队列的指针
    // 确定哪个队列是空的,哪个是非空的
    if(!QueueEmpty(empty))
    {
        empty = &obj->q2;
        noEmpty = &obj->q1;
    }
    // 将元素从非空队列转移到空队列,直到只剩下一个元素
    while(QueueSize(noEmpty) > 1)
    {
        QPush(empty, QueueFront(noEmpty));
        QPop(noEmpty);
    }
    // 弹出并返回最后一个元素
    int top = QueueFront(noEmpty);
    QPop(noEmpty);
    return top;
}

获取栈顶元素(Top):

栈顶元素对应于最后进入非空队列的元素。可以通过查看非空队列的尾部元素来得知栈顶元素。

c 复制代码
// 获取栈顶元素
int myStackTop(MyStack* obj)
{
    assert(obj); // 确保栈对象非空
    // 返回非空队列的尾部元素(栈顶元素)
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

检查栈是否为空(Empty):

如果两个队列都为空,那么栈为空。

c 复制代码
// 检查栈是否为空
bool myStackEmpty(MyStack* obj) 
{  
    assert(obj); // 确保栈对象非空
    // 如果两个队列都为空,栈为空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

销毁栈(Free):

释放栈所使用的资源,包括两个队列和栈本身的内存。

c 复制代码
// 释放栈所占用的资源
void myStackFree(MyStack* obj) 
{
    assert(obj); // 确保栈对象非空
    // 销毁两个队列
    QDestroy(&obj->q1);
    QDestroy(&obj->q2);
    // 释放栈对象所占用的内存
    free(obj);
}

以下是队列的实现:

c 复制代码
typedef int QDataType; // 定义队列数据类型为int

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

// 队列的结构体定义
typedef struct Queue
{
  QNode* phead; // 指向队列头部的指针
  QNode* ptail; // 指向队列尾部的指针
  int size; // 队列的大小
} Queue;

// 函数声明
void QInit(Queue* pq); // 初始化队列
void QDestroy(Queue* pq); // 销毁队列

void QPush(Queue* pq, QDataType x); // 向队列中添加元素
void QPop(Queue* pq); // 从队列中移除元素

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

bool QueueEmpty(Queue* pq); // 检查队列是否为空
int QueueSize(Queue* pq); // 获取队列的大小

// 初始化队列
void QInit(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  pq->phead = pq->ptail = NULL; // 将头指针和尾指针都设为NULL
  pq->size = 0; // 将队列大小设置为0
}

// 销毁队列
void QDestroy(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  QNode* cur = pq->phead;
  while (cur)
  {
    QNode* next = cur->next; // 保存下一个节点
    free(cur); // 释放当前节点
    cur = next; // 移动到下一个节点
  }
  pq->phead = NULL; // 将头指针设为NULL
  pq->ptail = NULL; // 将尾指针设为NULL
  pq->size = 0; // 将队列大小设置为0
}

// 向队列中添加元素
void QPush(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; // 新节点的下一个节点为NULL
  if (pq->ptail == NULL) // 如果队列为空
  {
    pq->phead = pq->ptail = newNode; // 队列头尾都指向新节点
  }
  else
  {
    pq->ptail->next = newNode; // 将新节点接到队列尾部
    pq->ptail = newNode; // 更新尾指针
  }
  pq->size++; // 队列大小增加
}

// 从队列中移除元素
void QPop(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  assert(pq->phead); // 断言队列不为空
  QNode* Del = pq->phead; // 保存要删除的节点
  pq->phead = pq->phead->next; // 更新头指针
  free(Del); // 释放节点内存
  if (pq->phead == NULL) // 如果队列变空
  {
    pq->ptail = NULL; // 更新尾指针
  }
  pq->size--; // 队列大小减少
}

// 获取队列头部元素的值
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; // 返回尾部元素的值
}

// 检查队列是否为空
bool QueueEmpty(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  return pq->phead == NULL; // 如果头指针为NULL,则队列为空
}

// 获取队列的大小
int QueueSize(Queue* pq)
{
  return pq->size; // 返回队列的大小
}

以下是本题的实现:

c 复制代码
// 定义一个使用两个队列模拟的栈的结构体
typedef struct
{
    Queue q1; // 第一个队列
    Queue q2; // 第二个队列
} MyStack;

// 创建一个新的栈
MyStack* myStackCreate() 
{
    // 动态分配内存给新栈
    MyStack* newStack =(MyStack*)malloc(sizeof(MyStack));
    if (!newStack)
    {
        perror("malloc fail"); // 如果内存分配失败,输出错误信息并退出
        exit(-1);
    }

    // 初始化两个队列
    QInit(&(newStack->q1));
    QInit(&(newStack->q2));
    return newStack;  
}

// 将一个元素推入栈中
void myStackPush(MyStack* obj, int x) 
{
    assert(obj); // 确保栈对象非空
    // 总是将元素推入非空队列中
    if(!QueueEmpty(&obj->q1))
    {
        QPush(&obj->q1, x);
    }
    else
    {
        QPush(&obj->q2, x);
    }
}

// 从栈中弹出一个元素
int myStackPop(MyStack* obj) 
{ 
    assert(obj); // 确保栈对象非空
    Queue* empty = &obj->q1; // 一个指向可能为空的队列的指针
    Queue* noEmpty = &obj->q2; // 一个指向非空队列的指针
    // 确定哪个队列是空的,哪个是非空的
    if(!QueueEmpty(empty))
    {
        empty = &obj->q2;
        noEmpty = &obj->q1;
    }
    // 将元素从非空队列转移到空队列,直到只剩下一个元素
    while(QueueSize(noEmpty) > 1)
    {
        QPush(empty, QueueFront(noEmpty));
        QPop(noEmpty);
    }
    // 弹出并返回最后一个元素
    int top = QueueFront(noEmpty);
    QPop(noEmpty);
    return top;
}

// 获取栈顶元素
int myStackTop(MyStack* obj)
{
    assert(obj); // 确保栈对象非空
    // 返回非空队列的尾部元素(栈顶元素)
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

// 检查栈是否为空
bool myStackEmpty(MyStack* obj) 
{  
    assert(obj); // 确保栈对象非空
    // 如果两个队列都为空,栈为空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

// 释放栈所占用的资源
void myStackFree(MyStack* obj) 
{
    assert(obj); // 确保栈对象非空
    // 销毁两个队列
    QDestroy(&obj->q1);
    QDestroy(&obj->q2);
    // 释放栈对象所占用的内存
    free(obj);
}
相关推荐
夜雨翦春韭2 小时前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
一直学习永不止步2 小时前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
戊子仲秋3 小时前
【LeetCode】每日一题 2024_10_2 准时到达的列车最小时速(二分答案)
算法·leetcode·职场和发展
￴ㅤ￴￴ㅤ9527超级帅4 小时前
LeetCode hot100---二叉树专题(C++语言)
c++·算法·leetcode
liuyang-neu4 小时前
力扣 简单 110.平衡二叉树
java·算法·leetcode·深度优先
一个不知名程序员www4 小时前
leetcode面试题17.04:消失的数字(C语言版)
leetcode
penguin_bark4 小时前
LCR 068. 搜索插入位置
算法·leetcode·职场和发展
爱上语文7 小时前
Java LeetCode每日一题
java·开发语言·leetcode
大二转专业12 小时前
408算法题leetcode--第24天
考研·算法·leetcode
__AtYou__19 小时前
Golang | Leetcode Golang题解之第448题找到所有数组中消失的数字
leetcode·golang·题解