服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言栈与队列相互实现详解

C语言栈与队列相互实现详解

2022-11-07 13:38李逢溪 C/C++

栈和队列,严格意义上来说,也属于线性表,因为它们也都用于存储逻辑关系为 "一对一" 的数据,但由于它们比较特殊,本章讲解分别用队列实现栈与用栈实现队列

一、本章重点

  • 用两个队列实现栈
  • 用两个栈实现队列
  • 解题思路总结

二、队列实现栈

C语言栈与队列相互实现详解

我们有两个队列:

C语言栈与队列相互实现详解

入栈数据1、 2、 3

可以将数据入队列至队列一或者队列二。

C语言栈与队列相互实现详解

如何出栈?

但出栈要先出1,怎么办?

第一步:

将队列一出队n-1个至队列二。

C语言栈与队列相互实现详解

第二步:

pop队列一的最后一个元素。

C语言栈与队列相互实现详解

接下来怎么入栈呢?

将元素入队至不为空的队列。

怎么判断栈空?

队列一和队列二都为空的情况下,栈就是空的。

如何取栈顶元素?

取不为空的队列尾部元素。

总的来说就是,入栈时就将数据插入不为空的队列,出栈就将不为空的队列的前n-1个数据导入至另一个队列,然后pop最后一个元素。

代码实现:

首先我们要构造一个栈。

这个栈要包含两个队列

typedef struct 
{
  Queue q1;
  Queue q2;
} MyStack;

在此之前我们要准备好队列的一般接口:

我这里的队列是用单链表来构建的,具体接口实现可以看我之前的文章。

typedef int QTypeData;
typedef struct QueueNode
{
	struct QueueNode* next;
	QTypeData val;
}QN;

void QueueInit(Queue* pq)//初始化队列
size_t QueueSize(Queue* pq)//求队列元素个数
int QueueBack(Queue* pq)//取队列尾部数据
void QueuePush(Queue* pq, QTypeData x)//将x入队
void QueuePop(Queue* pq)//出队
void QueueDestroy(Queue* pq)//结束队列

我们要用队列实现栈的接口:

  • 第一个接口:创建并初始化栈

接口一:MyStack* myStackCreate()

这样做行吗?

MyStack* myStackCreate()
{

  MyStack ms;
  QueueInit(&ms.q1);
  QueueInit(&ms.q2);
  return ms;
}

很显然,返回局部变量的地址是不明智的,因为在函数返回时,ms开辟的空间会重新交给操作系统,再次访问就是非法操作。

因此我们需要将ms开辟在堆区。

参考代码:

  • 第二个接口:入栈

接口二:void myStackPush(MyStack* obj, int x)

入栈简单:只要将数据插入到不为空的队列即可。

入栈之前我们需要判断队列满吗?

不需要,因为我的队列是用单链表实现的,可以无限链接下去。

如果两个队列都为空,该插入到哪个队列呢?

我们可以这样做,如果队列1为空,就插入到队列2,如果不为空,就插入到队列1.

参考代码:

  • 第三个接口:出栈

接口三:int myStackPop(MyStack* obj) //出栈

再次回顾一下我们是如何出栈的。

首先我们有两个队列

初始状态它们都是空的

队列一:空

队列二:空

入栈1、2、3、4

执行后

队列一:空

队列二:1、2、3、4

出队列只能先出1,如何出4呢?

把1、2、3先出给队列一

执行后

队列一:1、2、3

队列二:4

然后将4用变量记录并出队,最后返回记录4的变量。

执行后

队列一:1、2、3

队列二:空。

出队三板斧

第一步:即将不为空的队列的前n-1个元素入至为空的队列。

第二步:将剩下的1个元素用变量记录,然后将最后一个元素出队。

第三步:返回用变量记录的元素。

需要注意的是:如果栈为空,那么就返回false。

参考代码:

  • 第四个接口:取栈顶元素

接口四:int myStackTop(MyStack* obj) //取栈顶元素

取栈顶元素之前我们需要保证栈不为空

如果栈为空,返回false。

取栈顶元素,即取不为空的队列的队尾元素。

参考代码:

  • 第五个接口:判断栈是否为空

接口五:bool myStackEmpty(MyStack* obj) //判断栈是否为空

如果两个队列都是空的那么该栈就是空的。

这里多提一下,栈的元素个数怎么求?

栈的元素个数就是那个不为空队列的元素个数。

参考代码:

  • 第六个接口:销毁栈

接口六:void myStackFree(MyStack* obj) //结束栈

参考代码:

void myStackFree(MyStack* obj) 
{
  QueueDestroy(&obj->q1);
  QueueDestroy(&obj->q2);
  free(obj);
}

最后需要注意的是:调用栈为空的接口时,要先声明!!

 

三、栈实现队列

C语言栈与队列相互实现详解

第一次入队

C语言栈与队列相互实现详解

将数据1出队操作

将栈1的数据全导入栈2

C语言栈与队列相互实现详解

然后栈2进行出栈操作

C语言栈与队列相互实现详解

再次入队5、6、7

直接将5、6、7入栈至栈1

C语言栈与队列相互实现详解

实际我们的对头和队尾是这样的

C语言栈与队列相互实现详解

总的来说:

用两个栈实现一个队列,就得把一个栈专门入队,另一个栈用作出队。这里实现的时候我们用栈1做入队栈,栈2做出队栈。

也就是说,只要将数据入队,直接放入栈1.

出队就直接出栈2的数据,如果栈2为空,这将栈1的数据全部导入栈2.

队列的结构体:

typedef struct 
{
  ST st1;
  ST st2;
} MyQueue;

我们需要准备的栈

typedef int STDataType;
typedef struct Stack
{
	STDataType* data;
	int top;
	int capacity;
}ST;

这里我用的是动态数组实现的栈

需要提前准备栈的接口:

void STInit(ST* p)
void STDestroy(ST* p)
void STPush(ST* p,STDataType x)
void STPop(ST* p)
bool STEmpty(ST* p)
int STSize(ST* p)
STDataType STRear(ST* p)
  • 第一个接口:创建并初始化队列
MyQueue* myQueueCreate() 

同样的,需要把队列开辟在堆区,同时对栈1和栈2进行初始化。

参考代码:

MyQueue* myQueueCreate() 
{
  MyQueue* mq=(MyQueue*)malloc(sizeof(MyQueue));
  assert(mq);
  STInit(&mq->st1);
  STInit(&mq->st2);
  return mq;
}
  • 第二个接口:入队
void myQueuePush(MyQueue* obj, int x) 

我们把栈1做入队栈,栈2做出队栈。

也就是说,只要将数据入队,直接放入栈1.

出队就直接出栈2的数据,如果栈2为空,这将栈1的数据全部导入栈2.

参考代码:

void myQueuePush(MyQueue* obj, int x) 
{
  STPush(&obj->st1,x);
}
  • 第三个接口:出队
int myQueuePop(MyQueue* obj) 

先要判断队列是否为空,如果队列为空则返回false。

其次如果栈2为空,就将栈1的数据全导入栈2.

参考代码:

int myQueuePop(MyQueue* obj) 
{
  if(myQueueEmpty(obj))
  {
      return false;
  }
  if(STEmpty(&obj->st2))
  {
      int n=STSize(&obj->st1);
      while(n--)
      {
          STPush(&obj->st2,STRear(&obj->st1));
          STPop(&obj->st1);
      }
  }
  int ret=STRear(&obj->st2);
  STPop(&obj->st2);
  return ret;
}

第四个接口:取队头元素

int myQueuePeek(MyQueue* obj)

取队头元素之前,要判断队列是否为空,如果为空,则返回false

队头元素即栈2的尾部元素。

C语言栈与队列相互实现详解

但如果栈2为空呢?

那队列的队头元素就是栈1的头部元素。

参考代码:

int myQueuePeek(MyQueue* obj) 
{
  if(myQueueEmpty(obj))
  {
      return false;
  }
  if(STEmpty(&obj->st2))
  {
      return STFront(&obj->st1);
  }
  return STRear(&obj->st2);
}

第五个接口:判断队列是否为空

bool myQueueEmpty(MyQueue* obj)

队列为空,需要栈1和栈2都为空。

参考代码:

bool myQueueEmpty(MyQueue* obj) 
{
  return STEmpty(&obj->st1) && STEmpty(&obj->st2);
}

第六个接口:销毁队列

void myQueueFree(MyQueue* obj) 

参考代码:

void myQueueFree(MyQueue* obj) 
{
  STDestroy(&obj->st1);
  STDestroy(&obj->st2);
  free(obj);
}

注意:当使用判断队列是否为空的接口时,注意是否在之前声明过了。

 

四、解题思路总结

1.用队列实现栈:

我们需要用两个队列实现栈

栈类是于尾插尾删

队列是尾插头删

第一次入栈:两个队列都为空,随便插入一个队列都可

第一次出栈:出栈要出的是尾部数据,队列只能出头部数据,这是队列不能直接实现的。

那么需要将不为空的队列前n-1个数据导入至为空的队列,再将最后一个元素pop掉。

队列一:1、2、3、4

队列二:空

那么导入后

队列一:4

队列二:1、2、3

最后pop最后一个元素

队列一:空

队列二:1、2、3、4

再次尾插:尾插至不为空的队列即可。

2.用栈实现队列

我们有两个栈要求实现队列的一般接口

栈一:空

栈二:空

第一次入队:入栈至栈一或者栈二都可,这里选择栈一。

假设入队1、2、3、4

栈一:1、2、3、4

栈二:空

出队:要先出1

将栈一全部导入栈二

栈一:空

栈二:4、3、2、1

之后入队就插入至栈一

出队就pop栈二。

C语言栈与队列相互实现详解

到此这篇关于C语言栈与队列相互实现详解的文章就介绍到这了,更多相关C语言 栈与队列内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/m0_62171658/article/details/123805478

延伸 · 阅读

精彩推荐
  • C/C++c语言 汉诺塔算法代码

    c语言 汉诺塔算法代码

    c语言 汉诺塔算法代码,需要的朋友可以参考一下...

    C语言教程网1692020-11-22
  • C/C++C语言与Lua之间的相互调用详解

    C语言与Lua之间的相互调用详解

    这篇文章主要给大家介绍了关于C语言与Lua之间的相互调用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值...

    那个少年6662021-06-15
  • C/C++C语言宏定义#define的使用

    C语言宏定义#define的使用

    本文主要介绍了C语言宏定义#define的使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    南城Flipped6542022-08-14
  • C/C++C语言、C++内存对齐问题详解

    C语言、C++内存对齐问题详解

    这篇文章主要介绍了C语言、C++内存对齐问题详解,内存对齐的问题主要存在于理解struct和union等复合结构在内存中的分布,需要的朋友可以参考下...

    果冻想6342021-02-05
  • C/C++Recommended C Style and Coding Standards中文翻译版

    Recommended C Style and Coding Standards中文翻译版

    本文翻译自Recommended C Style and Coding Standards(C语言编码风格和标准),需要的朋友可以参考下...

    C语言程序设计3722021-01-19
  • C/C++C++实现LeetCode(48.旋转图像)

    C++实现LeetCode(48.旋转图像)

    这篇文章主要介绍了C++实现LeetCode(48.旋转图像),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...

    Grandyang4642021-11-26
  • C/C++C语言关于自定义数据类型之枚举和联合体详解

    C语言关于自定义数据类型之枚举和联合体详解

    枚举顾名思义就是把所有的可能性列举出来,像一个星期分为七天我们就可以使用枚举,联合体是由关键字union和标签定义的,和枚举是一样的定义方式,...

    Ersansui10142022-02-28
  • C/C++简单谈谈c/c++中#import、#include和@class的区别

    简单谈谈c/c++中#import、#include和@class的区别

    对于#import,我想做过iOS开发的人应该都不陌生。在开发过程中,当我们需要声明某一个类时,都需要去引用。而#imclude的话,在我们学习C时就已经知道了,...

    Mr.Lin4702021-04-13