数据结构:队列和栈的相互实现

文章目录

  • 前言
  • 一、队列实现栈
    • [1.2 代码的实现](#1.2 代码的实现)
      • 1.2.1结构体的构建
      • [1.2.2 初始化](#1.2.2 初始化)
      • [1.2.3 模型入栈](#1.2.3 模型入栈)
      • [1.2.4 模拟出栈并返回栈顶元素](#1.2.4 模拟出栈并返回栈顶元素)
      • [1.2.5 获取栈顶元素](#1.2.5 获取栈顶元素)
      • [1.2.6 判空](#1.2.6 判空)
      • [1.2.7 释放](#1.2.7 释放)
  • [二 栈实现队列](#二 栈实现队列)
    • [2.1 思路分析](#2.1 思路分析)
    • [2.2 代码实现](#2.2 代码实现)
      • [2.2.1 结构体的构建](#2.2.1 结构体的构建)
      • [2.2.2 初始化](#2.2.2 初始化)
      • [2.2.3 将元素 x 推到队列的末尾(模拟入队)](#2.2.3 将元素 x 推到队列的末尾(模拟入队))
      • [2.2.4 返回队列开头的元素](#2.2.4 返回队列开头的元素)
      • [2.2.5 从队列的开头移除并返回元素(模拟出队)](#2.2.5 从队列的开头移除并返回元素(模拟出队))
      • [2.2.6 判空](#2.2.6 判空)
      • [2.2.7 销毁](#2.2.7 销毁)

前言

虽然在实践中没有什么意义,但是可以帮助我们熟悉和了解栈和队列


一、队列实现栈

思路

如下图所示,队列的规则是先进先出,而栈的规则是后进先出;如图中q1我们入队1、2、3、4;按照栈我们出栈的是4,因此我们将q1中的红色框中的数据导给q2,然后剩下4,4直接出队就达到了效果。

当我们出完4后,当我们想入数据,是给q1,还是q2呢?

q2,假设我们入给q1,如下图所示,然后我们需要6出队,该怎么出呢?是不是想将5导入到q2,再出6;那如果我再出5,你是不是要让q2的1、2、3、4导给q1,然后出5。如果再进行push呢,又给q2吗?这样会一直导来导去,逻辑会混乱。那栈顶的数据我们就不知道是在q1,还是在q2。因此,我们需要向非空的队列中去push数据。

1.2 代码的实现

首先需要了解队列的实现

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int QDataTpye;
typedef struct QueueNode
{
	QDataTpye val;
	struct QueueNode* next;
}QueueNode;


typedef struct Queue
{
	QueueNode* ptail;
	QueueNode* phead;
	int size;
}Queue;

//初始化
void QueueInit(Queue* pq);

//队尾插入
void QueuePush(Queue* pq, QDataTpye x);

//队头删除
void QueuePop(Queue* pq);

//取队头队尾的数据
QDataTpye QueueFront(Queue* pq);
QDataTpye QueueBack(Queue* pq);

//个数
int QueueSize(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

//销毁
void QueueDestory(Queue* pq);

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataTpye x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("QueuePush::malloc");
		return;
	}

	newnode->next = NULL;
	newnode->val = x;

	//空的
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else//尾插
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);

	//一个节点
	if (pq->phead->next == NULL)
	{

		free(pq->phead);
		pq->phead = pq->ptail = NULL;

	}
	else
	{
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}

	pq->size--;

}

QDataTpye QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}

QDataTpye QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

void QueueDestory(Queue* pq)
{
	assert(pq);
	QueueNode* cur = NULL;
	cur = pq->phead;
	while (cur)
	{
		QueueNode* tem = cur->next;
		free(cur);
		cur = tem;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;

在实现队列的基础上再来用两个队列实现栈

1.2.1结构体的构建

首先我们需要封装一个结构体来管理这两个队列

c 复制代码
typedef struct MyStack
{
	Queue q1;
	Queue q2;
}MyStack;

1.2.2 初始化

对结构体进行初始化

c 复制代码
MyStack* MyStackCreat()
{
	MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
	if (pst == NULL)
	{
		perror("MyStackCreat::malloc");
		exit(1);
	}
	QueueInit(&(pst->q1));
	QueueInit(&(pst->q2));

	return pst;
}

1.2.3 模型入栈

谁不为空就入给谁,都为空随便入

c 复制代码
void myStackPush(MyStack*obj, QDataTpye x)
{
	if (!QueueEmpty(&(obj->q1)))
	{
		QueuePush(&(obj->q1), x);
	}
	else
	{
		QueuePush(&(obj->q2), x);
	}
}

1.2.4 模拟出栈并返回栈顶元素

c 复制代码
int myStackPop(MyStack* obj)
{
	//假设法
	MyStack* Empty = &(obj->q1);
	MyStack* nonEmpty = &(obj->q2);
	if (&(obj->q2) == NULL)
	{
		nonEmpty = &(obj->q1);
		Empty = &(obj->q2);
	}
	//不为空前size-1导走,删除最后一个就是栈顶数据
	while (QueueSize(nonEmpty) > 1)
	{
		QueuePush(Empty,QueueFront(nonEmpty));
		QueuePop(nonEmpty);
	}

	int top = QueueFront(nonEmpty);
	QueuePop(nonEmpty);

	return top;

}

1.2.5 获取栈顶元素

找到不为空的队列返回队尾元素就是栈顶元素

c 复制代码
int myStackTop(MyStack* obj)
{
	if (!QueueEmpty(&(obj->q1)))
	{
		return QueueBack((&(obj->q1)));
	}
	else
	{
		return QueueBack((&(obj->q2)));
	}
}

1.2.6 判空

也就是判断两个队列是否为空

c 复制代码
bool myStckEmpty(MyStack* obj)
{
	return QueueEmpty(&(obj->q2)) && QueueEmpty(&(obj->q1));
}

1.2.7 释放

直接释放obj没有,当我们直接释放掉obj时,除了释放掉了obj,后面的都没有释放掉,会造成内存泄漏

c 复制代码
void myStackFree(MyStack* obj)
{
	QueueDestory(&(obj->q1));
	QueueDestory(&(obj->q2));

	free(obj);
}

这里obj置不置空都行,它是一级指针,只起到传值的作用,改变不了实参。

二 栈实现队列

2.1 思路分析

首先我们需要明白栈是后进先出,而队列是先进先出。和上面队列实现栈一样,我们将一个不为空的栈导到另外一个栈去,然后出栈是不是就达到效果了。此时,第一个栈就变成空,如果我们再想入栈数据该怎么办呢?是向非空的栈入数据的话我们就需要保持之前的顺序,需要将第二个栈的数据导到第一个栈然后入栈数据,然后再将这个数据导入到第二个栈中出栈,这也是可以的,但是很麻烦。因此,我们只需要将一个栈作为入栈,另一个栈出栈就好了,比如之前导好后的数据,我们需要再入栈,就向最开始导的栈中入,然后等另外一个栈所有的数据完成出栈后,再将数据导入进去。

2.2 代码实现

首先我们需要在实现栈的基础上完成。

c 复制代码
typedef int STDataType;
typedef struct
{
	STDataType* arr;  //指向栈数组空间的指针
		int top;       //栈顶位置
		int capacity;   //容量
}Stack;

//栈的初始化
void StackInit(Stack* s);


//栈的销毁
void StackDestory(Stack* s);

//核心逻辑
//x元素入栈
void StackPush(Stack* s, STDataType x);


//将栈顶元素出栈,并返回栈顶元素
STDataType StackPop(Stack* s);

//获取栈顶元素并返回
STDataType StackTop(Stack* s);

//获取栈中有效元素个数
int StackSize(Stack* s);

//检测栈是否为空,如果是空返回真,否则返回假
bool StackEmpty(Stack* s);



void StackInit(Stack* s)
{
    assert(s);
    s->arr = (STDataType*)malloc(4 * sizeof(STDataType));//开辟四个元素的空间
    if (s->arr == NULL)
    {
        perror("StackInit::malloc");
        return;
    }
    // 初始化时,top = 0,表⽰top指向的是栈顶元素的下⼀个位置
    // 初始化时,top = -1,表⽰top指向的是栈顶元素
    s->top = 0;
    s->capacity = 0;
}

void StackPush(Stack* s, STDataType x)
{
    assert(s);
    //空间扩容
    if (s->capacity == s->top)
    {
        int newcapacity = s->capacity == 0 ? 4 : 2 * s->capacity;
        STDataType* tem = (STDataType*)realloc(s->arr, sizeof(STDataType) * newcapacity);
        if (tem == NULL)
        {
            perror("tem::realloc");
            exit(1);
        }
        s->arr = tem;
        s->capacity = newcapacity;
    }

    //入栈
    s->arr[s->top++] = x;
}

STDataType StackPop(Stack* s)
{
    assert(s);
    //判断是否为空,如为空就没有出栈的必要
    if (!StackEmpty(s))
    return s->arr[--s->top];
}

STDataType StackTop(Stack* s)
{
    assert(s);
    if (!StackEmpty(s))
        return s->arr[s->top - 1];//top=0,指向的是栈顶下一个位置
}

int StackSize(Stack* s)
{
    assert(s);
    return s->top;
}

bool StackEmpty(Stack* s)
{
    assert(s);
    return s->top == 0;
}

void StackDestory(Stack*s)
{
    assert(s);
    if(s->arr)
  {
    free(s->arr);
    s->arr = NULL;
    s->top = 0;
    s->capacity = 0;
  }
}

2.2.1 结构体的构建

在栈的结构上构建两个栈,一个栈作为入栈,一个栈作为出栈

c 复制代码
typedef struct STQ
{
	Stack s1;//栈1入栈
	Stack s2;//栈2出栈
}MyQueue;

2.2.2 初始化

c 复制代码
MyQueue* myQueueCreate()
{
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    if (obj == NULL)
    {
        perror("myQueueCreate::malloc");
        exit(1);
    }
    StackInit(&(obj->s1));
    StackInit(&(obj->s2));
    return obj;
}

2.2.3 将元素 x 推到队列的末尾(模拟入队)

c 复制代码
void myQueuePush(MyQueue* obj, int x)
{
    assert(obj);
    StackPush(&(obj->s1),x);
}

2.2.4 返回队列开头的元素

我们先确定s2是否为空,为空我们需要将s1的数据导入s2中,然后去s2的栈顶元素即可

c 复制代码
int myQueuePeek(MyQueue* obj)
{
    assert(obj);
    if (StackEmpty(&(obj->s2)))
    {
        //导数据
        while (!StackEmpty(&(obj->s1)))
        {
            int top = StackTop(&(obj->s1));
            StackPush(&(obj->s2), top);
            StackPop(&(obj->s1));
        }
    }
    return StackTop(&(obj->s2));
}

2.2.5 从队列的开头移除并返回元素(模拟出队)

这个和上面的2.2.4差不多,都需要导入数据,但是导入数据后只需要pop就可以了。

c 复制代码
int myQueuePop(MyQueue* obj)
{
    assert(obj);
    int top = myQueuePeek(&(obj->s2));
    StackPop(&(obj->s2));
    return top;   
}

2.2.6 判空

c 复制代码
bool myQueueEmpty(MyQueue* obj) 
{
    assert(obj);
    return StackEmpty(&(obj->s1)) && StackEmpty (&(obj->s1));
}

2.2.7 销毁

c 复制代码
void myQueueFree(MyQueue* obj) 
{
    StackDestory(&obj->s1);
    StackDestory(&obj->s2);
    free(obj);
}
相关推荐
牢姐与蒯5 小时前
双指针算法
数据结构·算法
Hesionberger5 小时前
LeetCode406:重建身高队列精髓解析
开发语言·数据结构·python·算法·leetcode
不一样的故事1269 小时前
军工行业合规与基础认知
数据结构·经验分享·算法
闪电悠米21 小时前
力扣hot100-56.合并区间-排序详解
数据结构·算法·leetcode·贪心算法·排序算法
yuannl101 天前
图的存储方式
数据结构
会编程的小孩1 天前
初识数据类型以及变量定义
数据结构·算法
梅梅绵绵冰1 天前
数据结构-时间复杂度
数据结构·算法
cpp_25011 天前
P6625 [省选联考 2020 B 卷] 卡牌游戏
数据结构·c++·算法·前缀和·贪心·洛谷题解·省选
起予者汝也1 天前
Python 数据结构
开发语言·数据结构·python