[数据结构] 用两个栈实现队列详解

简介: 在数据结构中,栈和队列是较为常见的两种数据结构。他们各有自己的特点:栈是后进先出原则,队列是先进先出原则。那怎么用栈去实现队列呢?此问题在面试中也是高频出现的问题。本篇文章会给出详细解释。

 在数据结构中,栈和队列是较为常见的两种数据结构。他们各有自己的特点:栈是后进先出原则,队列是先进先出原则。那怎么用栈去实现队列呢?此问题在面试中也是高频出现的问题。本篇文章会给出详细解释。



一、栈实现队列的特点分析

1、1 具体分析



 队列和栈在插入数据时,队列是从队尾进行插入,栈是从栈顶插入。但是他们的删除数据是不同的。我们知道队列的特点是:先新先出 ,删除数据是在对头进行删除,栈的特点是:先进后出,也就是在栈顶进行删除。


 当我们用栈实现队列时,最根本的也是最重要的是需要解决删除的问题。我们用栈实现队列时,在栈中的删除就不是删除栈顶的元素了,我们需要根据队列的特点进行删除,也就是我们需要删除的是栈底的元素。



63adcb5d7bc64755897f9da28f17b15a.png

怎么删除栈底的元素呢?在这里我们需要两个栈,分别命名为push栈pop栈。我们先把元素的插入全部插入到push栈中,当需要删除时,我们首先把push栈中的元素全部导入到pop栈中,此时的pop栈中的栈顶元素,相当于我们要删除的对头元素了。



f3d02df5549e4912b65f23b43c11e235.png


 那如果我们想要接着插入呢?我们接着往push栈中插入即可。删除的话我们看pop栈中是否有元素,如果pop栈不为空,就接着删除如果pop栈为空,我们需要把push栈的元素再次导入到pop栈中删除即可。具体流程我们可以结合下图(gif)理解:


81641a0c1931489f9da8d5039d14c175.gif





1、2 整体概括


 用栈模拟队列我们整体的思路分为以下几步:


我们需要先定义两个栈,分别为push栈和pop栈;

插入数据到往push栈中;

删除数据时,需要先判断pop栈是否为空。如果为空,需要将push栈的所有数据导入到pop栈中。如果不为空,就直接在pop栈删除即可。

再插入数据时,就往push栈中插入即可。

 以上即为用栈模拟队列的全过程,那我们来看代码的实现。


二、用栈模拟队列代码的实现

 这里我们用c语言进行实现。所以我们这里需要先手撕出一个栈。



2、1 手撕 栈 代码

2、1、1 stack.h

#define INT_CAPACITY 4
typedef int STDataType;
typedef struct stack
{
  STDataType* a;
  int top;
  int capacity;
}ST;
void STInit(ST* ps);
void STDestory(ST* ps);
bool STIsEmpty(ST* ps);
void STPush(ST* ps, STDataType x);
void STPop(ST* ps);
int STSize(ST* ps);
STDataType STTop(ST* ps);


2、1、2 stack.c

void STInit(ST* ps)
{
  assert(ps);
  ps->a = (STDataType*)malloc(sizeof(STDataType)* INT_CAPACITY);
  if (ps->a == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  ps->top = 0;
  ps->capacity = INT_CAPACITY;
}
void STDestory(ST* ps)
{
  assert(ps);
  free(ps->a);
  ps->capacity = 0;
  ps->top = 0;
}
bool STIsEmpty(ST* ps)
{
  assert(ps);
  return ps->top==0;
}
void STPush(ST* ps, STDataType x)
{
  assert(ps);
  if (ps->top == ps->capacity)
  {
    STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
    if (tmp == NULL)
    {
      perror("realloc fail");
      exit(-1);
    }
    ps->a = tmp;
    ps->capacity *= 2;
  }
  ps->a[ps->top++] = x;
}
void STPop(ST* ps)
{
  assert(ps);
  assert(!STIsEmpty(ps));
  ps->top--;
}
int STSize(ST* ps)
{
  assert(ps);
  return ps->top;
}
STDataType STTop(ST* ps)
{
  assert(ps);
  assert(!STIsEmpty(ps));
  return ps->a[ps->top - 1];
}


2、2 用栈实现队列代码

这个是OJ链接( 用栈实现队列 - OJ链接(力扣)),大家可以直接点开链接用其他语言做一下。我们看C语言的代码实现。

#define INT_CAPACITY 4
typedef int STDataType;
typedef struct stack
{
  STDataType* a;
  int top;
  int capacity;
}ST;
void STInit(ST* ps);
void STDestory(ST* ps);
bool STIsEmpty(ST* ps);
void STPush(ST* ps, STDataType x);
void STPop(ST* ps);
int STSize(ST* ps);
STDataType STTop(ST* ps);
void STInit(ST* ps)
{
  assert(ps);
  ps->a = (STDataType*)malloc(sizeof(STDataType)* INT_CAPACITY);
  if (ps->a == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  ps->top = 0;
  ps->capacity = INT_CAPACITY;
}
void STDestory(ST* ps)
{
  assert(ps);
  free(ps->a);
  ps->capacity = 0;
  ps->top = 0;
}
bool STIsEmpty(ST* ps)
{
  assert(ps);
  return ps->top==0;
}
void STPush(ST* ps, STDataType x)
{
  assert(ps);
  if (ps->top == ps->capacity)
  {
    STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
    if (tmp == NULL)
    {
      perror("realloc fail");
      exit(-1);
    }
    ps->a = tmp;
    ps->capacity *= 2;
  }
  ps->a[ps->top++] = x;
}
void STPop(ST* ps)
{
  assert(ps);
  assert(!STIsEmpty(ps));
  ps->top--;
}
int STSize(ST* ps)
{
  assert(ps);
  return ps->top;
}
STDataType STTop(ST* ps)
{
  assert(ps);
  assert(!STIsEmpty(ps));
  return ps->a[ps->top - 1];
}
typedef struct 
{
    ST StackPop;
    ST StackPush;
} MyQueue;
MyQueue* myQueueCreate()
{
    MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
    STInit(&obj->StackPop);
    STInit(&obj->StackPush);
    return obj;
}
void myQueuePush(MyQueue* obj, int x) 
{
    STPush(&obj->StackPush,x);
}
int myQueuePeek(MyQueue* obj) 
{
    if(STSize(&obj->StackPop)==0)
    {
        while(STSize(&obj->StackPush)!=0)
        {
            STPush(&obj->StackPop,STTop(&obj->StackPush));
            STPop(&obj->StackPush);
        }
    }
    return STTop(&obj->StackPop);
}
int myQueuePop(MyQueue* obj) 
{
    int ret=myQueuePeek(obj);
    STPop(&obj->StackPop);
    return ret;
}
bool myQueueEmpty(MyQueue* obj) 
{
    return STSize(&obj->StackPop)==0 && STSize(&obj->StackPush)==0;
}
void myQueueFree(MyQueue* obj) 
{
    STDestory(&obj->StackPop);
    STDestory(&obj->StackPush);
    free(obj);
}


相关文章
|
2天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
|
3天前
|
Java
【数据结构】栈和队列的深度探索,从实现到应用详解
本文介绍了栈和队列这两种数据结构。栈是一种后进先出(LIFO)的数据结构,元素只能从栈顶进行插入和删除。栈的基本操作包括压栈、出栈、获取栈顶元素、判断是否为空及获取栈的大小。栈可以通过数组或链表实现,并可用于将递归转化为循环。队列则是一种先进先出(FIFO)的数据结构,元素只能从队尾插入,从队首移除。队列的基本操作包括入队、出队、获取队首元素、判断是否为空及获取队列大小。队列可通过双向链表或数组实现。此外,双端队列(Deque)支持两端插入和删除元素,提供了更丰富的操作。
10 0
【数据结构】栈和队列的深度探索,从实现到应用详解
|
7天前
|
Linux C++ Windows
栈对象返回的问题 RVO / NRVO
具名返回值优化((Name)Return Value Optimization,(N)RVO)是一种优化机制,在函数返回对象时,通过减少临时对象的构造、复制构造及析构调用次数来降低开销。在C++中,通过直接在返回位置构造对象并利用隐藏参数传递地址,可避免不必要的复制操作。然而,Windows和Linux上的RVO与NRVO实现有所不同,且接收栈对象的方式也会影响优化效果。
|
22天前
|
存储 安全 编译器
缓冲区溢出之栈溢出(Stack Overflow
【8月更文挑战第18天】
46 3
|
23天前
|
测试技术
【初阶数据结构篇】栈的实现(附源码)
在每一个方法的第一排都使用assert宏来判断ps是否为空(避免使用时传入空指针,后续解引用都会报错)。
|
10天前
crash —— 获取内核地址布局、页大小、以及栈布局
crash —— 获取内核地址布局、页大小、以及栈布局
|
10天前
|
存储 程序员 C语言
堆和栈之间有什么区别
【9月更文挑战第1天】堆和栈之间有什么区别
77 0
|
19天前
|
机器学习/深度学习 消息中间件 缓存
栈与队列的实现
栈与队列的实现
35 0
|
23天前
|
测试技术
【初阶数据结构篇】队列的实现(赋源码)
首先队列和栈一样,不能进行遍历和随机访问,必须将队头出数据才能访问下一个,这样遍历求个数是不规范的。
|
28天前
|
算法 C语言 C++
【practise】栈的压入和弹出序列
【practise】栈的压入和弹出序列