当前位置: 首页 > news >正文

剑指offer----C语言版----第六天

目录

1. 用栈实现队列

         1.1 题目描述

1.2 栈和队列的基础知识

1.3 思路分析

2. 扩展题目——用队列实现栈

2.1 题目描述

2.2 思路分析


1. 用栈实现队列

原题链接:

剑指 Offer 09. 用两个栈实现队列 - 力扣(LeetCode)icon-default.png?t=MBR7https://leetcode.cn/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/submissions/

1.1 题目描述

用两个栈实现一个队列。请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

1.2 栈和队列的基础知识

栈是一种非常常见的数据结构,他在计算机领域被广泛应用,比如操作系统会给每一个线程创建一个栈用来存储函数调用时各个函数的参数,返回地址及临时变量等。栈的特点是先进后出,即最后被压入栈的元素会被第一个弹出。栈一般用顺序表实现。

栈通常是一个不考虑排序的数据结构,我们需要O(N)的时间复杂度才能找到一个元素,如果想要在O(1)的时间内找到,那么栈需要进行特殊处理。等刷题刷到后面再讲!

队列呢?与栈似乎截然不同,队列的特点是先进先出,即第一个入队列的元素会第一个出来。队列的实现我们一般用一个带头指针和尾指针的链表来实现。

1.3 思路分析

一个队列里面包含了两个栈,stack1和stack2。直讲解有点抽象,我们用一个具体的例子来讲解。

假设我们要向队列中插入,1,2,3三个元素后将它们删除,如何实现呢?

首先,我们插入1这个元素,不妨把他插入到stack1,然后再依次插入,2,3,此时stack1内栈顶元素为3,stack2为空(见下图)。

这时候,我们尝试从队列中删除一个元素。按照队列先入先出的规则,最先入列的1应该是最先出列的,故应该删除1。但是1存储在stack1中并不是在栈顶,于是我们需要借助stack2:如果我们把stack1中的元素依次弹出,并压入到stack2中,则stack2中的元素顺序正好和原来相反。因此经过三次弹出stack1和压入stack2的操作后,stack1为空,stack2中的元素是 3,  2,  1,这时候就可以弹出栈顶的1了。

在删除队头的1后,队列中还剩2和3,我们想要继续删除队头的元素,那么应该删除2,而此时2恰好又在栈顶,因此直接弹出2即可。

 

 通过例子的分析我们就可以总结如下规律:

添加元素的步骤:直接将元素入栈到stack1即可。

删除元素的步骤:当stack2不为空时,在栈顶的元素就是队列中先入列的元素,直接弹出栈顶的元素即可;当stack2为空时,我们就需要将stack1中的元素依次弹出并压入到stack2中,直到stack1为空。如果说stack2为空,去stack1中拿元素时发现stack1也为空,根据题目要求返回-1即可。

销毁队列:销毁两个栈即可。

//栈的代码
/
typedef int ST_DATA_TYPE;

typedef struct Stack
{
	ST_DATA_TYPE* data;
	int size;
	int capacity;
} ST;

//栈的初始化
void StackInit(ST* st)
{
	ST_DATA_TYPE* newlist = (ST_DATA_TYPE*)malloc(sizeof(ST_DATA_TYPE) * 4);
	if (newlist != NULL)
	{
		st->data = newlist;
		st->capacity = 4;
		st->size = 0;
	}
}

//销毁栈
void StackDestory(ST* st)
{
	free(st->data);
	st->data = NULL;
}

//入栈
void StackPush(ST* st, ST_DATA_TYPE x)
{
	if (st->size == st->capacity)
	{
		ST_DATA_TYPE* newlist = (ST_DATA_TYPE*)realloc(st->data, sizeof(ST_DATA_TYPE) * 2 * st->capacity);
		if (newlist == NULL)
		{
			perror("malloc");
		}
		else
		{
			st->data = newlist;
			st->capacity *= 2;
		}
	}
	st->data[st->size] = x;
	st->size++;

}

//出栈
void StackPop(ST* st)
{
	assert(st->size > 0);
	st->size--;
}

//查看栈顶元素
ST_DATA_TYPE StackTop(ST* st)
{
	assert(st->data);
	return st->data[st->size - 1];
}

//栈的大小
int StackSize(ST* st)
{
	assert(st->data);
	return st->size;
}

//判断栈是否为空
bool StackEmpty(ST* st)
{
	assert(st->data);
	return st->size == 0;
}

/

//定义队列
typedef struct {
	ST stack1;
	ST stack2;
} CQueue;


//初始化队列
CQueue* cQueueCreate() {
	CQueue* q1 = (CQueue*)malloc(sizeof(CQueue));
	StackInit(&q1->stack1);
	StackInit(&q1->stack2);
	return q1;
}

//入列
void cQueueAppendTail(CQueue* obj, int value) {
	StackPush(&obj->stack1, value);
}

//出列
int cQueueDeleteHead(CQueue* obj) {
	assert(obj);
	if (StackEmpty(&obj->stack2))
	{
        if(StackEmpty(&obj->stack1))
        {
            return -1;
        }
        else
        {
            while (!StackEmpty(&obj->stack1))
		    {
                int top = StackTop(&obj->stack1);
                StackPop(&obj->stack1);
                StackPush(&obj->stack2, top);
		    }
        }
	}
	int top = StackTop(&obj->stack2);
	StackPop(&obj->stack2);
	return top;
}

//销毁队列
void cQueueFree(CQueue* obj) {
	StackDestory(&obj->stack1);
	StackDestory(&obj->stack2);
}

2. 扩展题目——用队列实现栈

原题链接:

力扣icon-default.png?t=MBR7https://leetcode.cn/problems/implement-stack-using-queues/

2.1 题目描述

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

void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
bool empty() 如果栈是空的,返回 true ;否则,返回 false 。

2.2 思路分析

一个栈里面包含了两个队列,Queue1和Queue2,直接讲解也是有点抽象,我们通过例子来讲解:

假设我们要依次入栈1,  2,3

首先入栈元素1,同样,我们不妨直接把他插入Queue1中,然后再次插入2,  3到Queue1中,此时Queue1中有三个元素1,  2,  3,Queue2为空。

 现在,我们尝试从栈中删除一个元素。按照栈后进先出的规则,最后入栈的元素3应该最先出栈。但是元素3并不在Queue1的队头,我们不能直接把他删除。这时候我们就要借助Queue2了:将元素1从Queue1中出列,将它入列到Queue2中;将元素2从Queue1中出列将它入列到Queue2中,此时我们会发现,要出栈的元素3就在Queue1的队头了,直接将其出列即可。

 同样地,我们还想删除一个元素,此时2就是栈顶元素,它不在Queue2的队头不能直接删除,同样需要借助另一个队列Queue1,将元素1从Queue2中出列,入列到Queue1中,此时要出栈的元素2,就在Queue2的队头了,直接删除即可。

 还想删除栈顶的元素1时,我们发现它就在Queue1的队头直接删除即可。

通过以上分析我们总结出一下规律:

删除元素:将不为空的那个队列数据的N-1(N为不为空的那个队列的元素个数/队列大小)个导入到为空的那个队列,剩下的那个元素即为栈顶元素,删除即可。

添加元素:将元素添加到那个不为空的队列中即可,如果两个都为空,随便添加到哪一个都行。

查看栈顶的元素:根据以上分析:不为空的那个队列队尾的数据就是栈顶的元素,我们直接查看队尾的元素即可。

判断栈是否为空:如果两个队列都为空,则栈为空,否则栈不为空。

销毁栈:销毁两个队列即可。

//队列的代码
/
typedef  int Q_DATA_TYPE;

typedef struct QueueNode
{
	struct QueueNode* next;
	Q_DATA_TYPE data;
} QNode;

typedef struct QueuePointer
{
	QNode* head;
	QNode* tail;

} QP;

void QueueInit(QP* qp)
{
	assert(qp);
	qp->head = qp->tail = NULL;
}

void QueuePush(QP* qp, Q_DATA_TYPE x)
{
	assert(qp);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc failed\n");
		exit(-1);
	}
	newnode->next = NULL;
	newnode->data = x;
	if (qp->head == NULL)
	{
		qp->head = qp->tail = newnode;
	}
	else
	{
		qp->tail->next = newnode;
		qp->tail = newnode;
	}
}

void QueuePop(QP* qp)
{
	assert(qp);
	assert(qp->head);
	if (qp->head->next == NULL)
	{
		free(qp->head);
		qp->head = qp->tail = NULL;
	}
	else
	{
		QNode* next = qp->head->next;
		free(qp->head);
		qp->head = next;
	}

}

Q_DATA_TYPE QueueFront(QP* qp)
{
	assert(qp);
	assert(qp->head);
	return qp->head->data;
}

Q_DATA_TYPE QueueBack(QP* qp)
{
	assert(qp);
	assert(qp->tail);
	return qp->tail->data;
}

bool QueueEmpty(QP* qp)
{
	assert(qp);
	return qp->head == NULL;
}

void QueueDestory(QP* qp)
{
	assert(qp);

	QNode* cur = qp->head;
	while (cur != NULL)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	qp->head = qp->tail = NULL;
}

int QueueSize(QP* qp)
{
	assert(qp);

	int size = 0;
	QNode* cur = qp->head;
	while (cur)
	{
		++size;
		cur = cur->next;
	}
	return size;
}
/

//定义栈
typedef struct {
    QP Queue1;
    QP Queue2;

} MyStack;

//栈的初始化
MyStack* myStackCreate() {
    MyStack* ps = (MyStack*)malloc(sizeof(MyStack));
    if(ps==NULL)
    {
        printf("malloc failed");
        exit(-1);
    }
    else
    {
        QueueInit(&ps->Queue1);
        QueueInit(&ps->Queue2);
    }
    return ps;
}

//入栈
void myStackPush(MyStack* obj, int x) {

    if(QueueEmpty(&obj->Queue1))
    {
        QueuePush(&obj->Queue2,x);
    }
    else
    {
        QueuePush(&obj->Queue1,x);
    }
}

//出栈
int myStackPop(MyStack* obj) {
    QP*emptyq = &obj->Queue1; //假设Queue1为空,Queue2不为空
    QP*noneemptyq = &obj->Queue2;
    if(!QueueEmpty(&obj->Queue1)) //用if对假设进行修正
    {
        emptyq= &obj->Queue2;
        noneemptyq = &obj->Queue1;
    }
    while(QueueSize(noneemptyq)>1) //将不为空的那个队列导入到为空的那个
    {
        QueuePush(emptyq,QueueFront(noneemptyq));
        QueuePop(noneemptyq);
    }
    int a = QueueFront(noneemptyq); //记录原来不为空的那个队列的对头元素,然后删除
    QueuePop(noneemptyq);
    return a;
}
//查看栈顶元素
int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->Queue1))
    {
        return QueueBack(&obj->Queue1);
    }
    else
    {
        return QueueBack(&obj->Queue2);
    }
}
//判断栈是否为空
bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->Queue1) && QueueEmpty(&obj->Queue2);
}
//销毁栈
void myStackFree(MyStack* obj) {
    QueueDestory(&obj->Queue1);
    QueueDestory(&obj->Queue2);
    free(obj);
}

 

相关文章:

  • Qt音视频开发08-ffmpeg内核优化(极速打开/超时回调/实时响应)
  • 网络安全一哥的奇安信发布了全球高级可持续威胁年度报告 值得学习
  • 13---SpringBoot整合JWT,实现登录和拦截
  • 4366. 上课睡觉
  • vue后台系统管理项目-echarts柱状图实现订单统计
  • 2022年博客之星排行榜 日榜 2023-01-01 博客之星总榜
  • 一名普通Java程序员的2022的总结和2023的展望
  • 【寒假每日一题】洛谷 P1079 [NOIP2012 提高组] Vigenère 密码
  • 【概率论】期末复习笔记:参数估计
  • k-means算法进行数据分析应用
  • 【数据结构】单链表(线性表)的实现
  • JavaScript 入门基础 - 流程控制(四)
  • 剑指offer----C语言版----第五天
  • 使用python实现跨年烟花代码
  • 【Linux】Linux下基本指令(三)
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • css属性的继承、初识值、计算值、当前值、应用值
  • Flannel解读
  • Intervention/image 图片处理扩展包的安装和使用
  • Invalidate和postInvalidate的区别
  • Java编程基础24——递归练习
  • Leetcode 27 Remove Element
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • uni-app项目数字滚动
  • Vue小说阅读器(仿追书神器)
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 力扣(LeetCode)357
  • 前端之Sass/Scss实战笔记
  • 用 Swift 编写面向协议的视图
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​力扣解法汇总946-验证栈序列
  • #{}和${}的区别?
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (论文阅读30/100)Convolutional Pose Machines
  • (三)docker:Dockerfile构建容器运行jar包
  • (转)Linux下编译安装log4cxx
  • ****Linux下Mysql的安装和配置
  • ./configure,make,make install的作用(转)
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET CLR Hosting 简介
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Core中的去虚
  • .Net IE10 _doPostBack 未定义
  • .Net mvc总结
  • .NET Reactor简单使用教程
  • .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数
  • @configuration注解_2w字长文给你讲透了配置类为什么要添加 @Configuration注解
  • @Data注解的作用
  • @我的前任是个极品 微博分析
  • [【JSON2WEB】 13 基于REST2SQL 和 Amis 的 SQL 查询分析器
  • [16/N]论得趣
  • [4.9福建四校联考]
  • [Android]Tool-Systrace
  • [BZOJ3757] 苹果树