【数据结构初阶】一个队列怎么实现栈~~OJ

简介: 【数据结构初阶】一个队列怎么实现栈~~OJ

1.用两个队列实现栈

用队列实现栈


a04c59162f314b5581a3b57ca8c19f69.png

思路:主要是“入栈”和“出栈”


左边的是栈,假设1234依次进栈,那么如果要用右边的两个队列实现栈的功能,就应该按照4321的次序出栈,按照题目要求得用两个队列实现4321出数据的形式,并且完成栈的初始化,判空,销毁等功能。


第一步:实现“入栈”,我们将要入的每一个数据都入在其中一个队列,而保持另一个为空


第二步:实现“出栈步骤1”,因为每出一个数据都要来回倒腾数据,使得这次这个队列为空,下一次就是另一个为空,所以这里为了方便,我们就不关心q1和q2谁为空,而是用空队列和非空队列来描述操作。


我们先取非空队列里的队头数据入到之前预留好的空队列中,然后将非空队列里队头数据出队列,依次类推,直到原来的非空队列中只剩一个元素,此时,原来非空队列里的前size-1个数据都保留到了原来的空队列中。


第三步:实现“出栈步骤2”,将原来的非空队列中的唯一一个数据4出队列,即完成一次出队列的过程。


739aa5297daa4f95949489f3ce6faaa3.png


这里有很多细节值得我们的注意:


1.模拟初始化栈的时候,在定义MyStack时不能定义一个局部变量,然后返回局部变量的地址,这是经典的返回栈空间地址的错误。(下面是错误的写法)


MyStack* myStackCreate() {
    MyStack obj;
    //...
    return &obj;
}

2.模拟出栈的时候,我们为了方便,定义出empty和noempty两个队列的指针,而非拷贝!

(下面是错误的写法)

int myStackPop(MyStack* obj) {
    Queue empty=obj->q1;
    Queue noempty=obj->q2;
    if(QueueEmpty(&obj->q2))
    {
        empty=obj->q2;
        noempty=obj->q1;
    }
    while(QueueSize(&noempty)>1)
    {
        QueuePush(&empty,QueueFront(noempty));
        QueuePop(&noempty);
    }
    int ret=QueueFront(&noempty);
    QueuePop(&noempty);
    return ret;
}

3.模拟销毁栈的时候 ,不能直接free(obj),这样释放掉的仅仅是MyStack这个结构体的变量,而结构体变量里的一些指针所指向的动态开辟的空间并不会被释放掉!!(下面是错误的写法)


而将QueueDestory(&obj->q1);QueueDestory(&obj->q2);调用Queue的函数一个一个将链表结点释放才能真正达到释放的目的~~

void myStackFree(MyStack* obj) {
     free(obj);
}

4c332e4c01064fb3aca947bb50c87221.png


下面是完整的正确的代码~~

typedef int QDateType;
typedef struct QueueNode
{
  struct QueueNode* next;
  QDateType val;
}QueueNode;
typedef struct Queue
{
  QueueNode* head;
  QueueNode* tail;
}Queue;
void QueueInit(Queue* ps);
void QueuePush(Queue* ps, QDateType x);
void QueuePop(Queue* ps);
QDateType QueueFront(Queue* ps);
QDateType QueueBack(Queue* ps);
bool QueueEmpty(Queue* ps);
int QueueSize(Queue* ps);
void QueueDestory(Queue* ps);
void QueueInit(Queue* ps)
{
  assert(ps);
  ps->head = ps->tail = NULL;
}
void QueueDestory(Queue* ps)
{
  assert(ps);
  QueueNode* cur = ps->head;
  while (cur)
  {
    QueueNode* next = cur->next;
    free(cur);
    cur = next;
  }
  ps->head = ps->tail = NULL;
}
//入队列:尾插
void QueuePush(Queue* ps,QDateType x)
{
  assert(ps);
  QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
  newnode->next = NULL;
  newnode->val = x;
  if (newnode == NULL)
  {
    perror("malloc fail.");
    exit(-1);
  }
  if (ps->tail == NULL)
  {
    ps->head = ps->tail = newnode;
  }
  else
  {
    ps->tail->next = newnode;
    ps->tail = ps->tail->next;
  }
}
void QueuePop(Queue* ps)
{
  assert(ps);
  assert(!QueueEmpty(ps));
  if (ps->head == ps->tail)
  {
    free(ps->head);
    ps->head = ps->tail = NULL;
  }
  else
  {
    QueueNode* next = ps->head->next;
    free(ps->head);
    ps->head = next;
  } 
}
QDateType QueueFront(Queue* ps)
{
  assert(ps);
  assert(!QueueEmpty(ps));
  return ps->head->val;
}
QDateType QueueBack(Queue* ps)
{
  assert(ps);
  assert(!QueueEmpty(ps));
  return ps->tail->val;
}
bool QueueEmpty(Queue* ps)
{
  assert(ps);
  return ps->tail == NULL;
}
int QueueSize(Queue* ps)
{
  assert(ps);
  int size = 0;
  QueueNode* cur = ps->head;
  while(cur)
  {
    ++size;
    cur = cur->next;
  }
  return size;
}
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;
MyStack* myStackCreate() {
    MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&obj->q1);
    QueueInit(&obj->q2);
    return obj;
}
void myStackPush(MyStack* obj, int x) {
    //代码书写方式1:
    // Queue* empty=&obj->q1;
    // Queue* noempty=&obj->q2;
    // if(QueueEmpty(&obj->q2))
    // {
    //     empty=&obj->q2;
    //     noempty=&obj->q1;
    // }
    // QueuePush(noempty,x);
    //代码书写方式2:
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}
int myStackPop(MyStack* obj) {
    Queue* empty=&obj->q1;
    Queue* noempty=&obj->q2;
    if(QueueEmpty(&obj->q2))
    {
        empty=&obj->q2;
        noempty=&obj->q1;
    }
    while(QueueSize(noempty)>1)
    {
        QueuePush(empty,QueueFront(noempty));
        QueuePop(noempty);
    }
    int ret=QueueFront(noempty);
    QueuePop(noempty);
    return ret;
}
int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else 
    {
        return QueueBack(&obj->q2);
    }
}
bool myStackEmpty(MyStack* obj) {
        return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
     QueueDestory(&obj->q1);
     QueueDestory(&obj->q2);
     free(obj);
}

变式:如何用一个队列实现栈


029a571dc91344699b56b9cb449fbadb.png

typedef struct {
    Queue q;
} MyStack;
MyStack* myStackCreate() {
    MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&obj->q);
    return obj;
}
void myStackPush(MyStack* obj, int x) {
   QueuePush(&obj->q,x);
   for(int i=0;i<QueueSize(&obj->q)-1;i++)
   {
       QueuePush(&obj->q,QueueFront(&obj->q));
       QueuePop(&obj->q);
   }
}
int myStackPop(MyStack* obj) {
    int top=QueueFront(&obj->q);
    QueuePop(&obj->q);
    return top;
}
int myStackTop(MyStack* obj) {
    return QueueFront(&obj->q);
}
bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->q);
}
void myStackFree(MyStack* obj) {
    QueueDestory(&obj->q);
    free(obj);
}

4f06736141054cf097b756278c9ef464.png

2.用两个栈实现队列

用两个栈实现队列

5fd61e8125b44c0c921d2c6e1fcfaa46.png


思路:两个栈,一个用作入数据PushST,一个用作出数据PopST,在栈内倒转两次就变成正序了~~


343ee7e6b7da4698808a0cdd08def3b6.png

“队列”入数据:直接入PushST栈


"队列"出数据:如果PopST栈为空,则将PushST栈内的数据先全部倒到PopST中,然后再在PopST中出数据;

                       如果PopST栈不为空,则直接在PopST中出数据。


需要特别注意的是:"队列"出数据的时候,如果PopST栈不为空,则直接在PopST中出数据。这样可以避免123先入栈后,出了一个3后,再入数据4的情况出错!


typedef struct {
    Stack PushST;
    Stack PopST;
} MyQueue;
MyQueue* myQueueCreate() {
    MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&obj->PushST);
    StackInit(&obj->PopST);
    return obj;
}
void myQueuePush(MyQueue* obj, int x) {
   StackPush(&obj->PushST,x);
}
//未复用myQueuePeek代码的书写代码方式1:
   int myQueuePop(MyQueue* obj) {
   if(StackEmpty(&obj->PopST))
   {
       while(!StackEmpty(&obj->PushST))
       {
           StackPush(&obj->PopST,StackTop(&obj->PushST));
           StackPop(&obj->PushST);
       }
   }
   int top=StackTop(&obj->PopST);
   StackPop(&obj->PopST);
   return top;
   }
   int myQueuePeek(MyQueue* obj);
//复用myQueuePeek代码的书写代码方式2:
// int myQueuePop(MyQueue* obj) {
// //    if(StackEmpty(&obj->PopST))
// //    {
// //        while(!StackEmpty(&obj->PushST))
// //        {
// //            StackPush(&obj->PopST,StackTop(&obj->PushST));
// //            StackPop(&obj->PushST);
// //        }
// //    }
//    int top=myQueuePeek(obj);
//    StackPop(&obj->PopST);
//    return top;
//    }
int myQueuePeek(MyQueue* obj) {
   if(StackEmpty(&obj->PopST))
   {
       while(!StackEmpty(&obj->PushST))
       {
           StackPush(&obj->PopST,StackTop(&obj->PushST));
           StackPop(&obj->PushST);
       }
   }
   return StackTop(&obj->PopST);
}
bool myQueueEmpty(MyQueue* obj) {
   return StackEmpty(&obj->PushST)&&StackEmpty(&obj->PopST);
}
void myQueueFree(MyQueue* obj) {
    StackDestory(&obj->PushST);
    StackDestory(&obj->PopST);
    free(obj);
}
目录
相关文章
|
10天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
81 9
|
4天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
1天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
8 1
|
7天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
9天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
32 4
|
13天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
26天前
数据结构(栈与列队)
数据结构(栈与列队)
16 1
|
27天前
【数据结构】-- 栈和队列
【数据结构】-- 栈和队列
14 0
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
27 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
初步认识栈和队列
初步认识栈和队列
57 10