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

news2024/11/15 4:12:25

目录

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);
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/133438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构集合框架与大O渐进表示法

作者:爱塔居的博客_CSDN博客-JavaSE领域博主 专栏:数据结构 作者简介:大三学生,希望跟大家一起进步!🌹 博客简介:了解数据结构,学习一些关于数据结构的前置知识 文章目录 目录 文章目…

【论文翻译】Deep Learning for Multi-view Stereo via Plane Sweep: A Survey(2021)

一、论文简述 1. 第一作者:Qingtian Zhu 2. 发表年份:2021 3. 发表期刊:CVPR 4. 关键词:MVS、深度学习、综述 5. 核心思想:读到的第一篇深度MVS的综述,总结的很好,内容涵盖了2021年前的研究…

遗传算法python进阶理解+论文复现(纯干货,附前人总结引路)

遗传算法python进阶理解论文复现(纯干货,附前人总结引路)一、简介和相关概念遗传算法简介相关概念介绍二、与其他智能优化算法的比较蚁群算法粒子群优化算法人工神经网络算法模拟退火算法鱼群算法三、必学知识(站在前人的肩膀上&a…

1214. 波动数列(推公式 + DP)

题目如下: 思路 or 题解: 我们可以设: 第一个数为 xxx d {a, -b} 那后续的数为:xd1x d_1xd1​ , xd1d)2x d_1 d_)2xd1​d)​2 … … xd1d2......dn−1x d_1 d_2 ... ... d_{n - 1}xd1​d2​......dn−1​ 根据题意和上面…

(Java高级教程)第二章Java多线程常见面试题-第三节:线程安全集合类和死锁

文章目录一:线程安全集合类(1)多线程环境下使用ArrayList(2)多线程环境使用队列(3)多线程使用哈希表二:死锁(1)概念(2)死锁产生的四个…

(小程序)会议OA项目-首页

目录 一、FIex布局简介 1.什么是flex布局? 2.flex属性 学习地址: 3.flex弹性布局演示 ① 容器的属性 ⑴ flex-direction属性 ⑵ flex-wrap属性 ⑶ flex-flow ⑷ justify-content属性 ⑸ align-items属性 ⑹ align-content属性 二、轮播图组件m…

微信记录怎么恢复?恢复已删除微信历史记录的4种方式

恢复已删除微信历史记录的4种方式 如何在有/没有备份的情况下在 iPhone 和 Android 上恢复旧的或已删除的微信历史记录,如聊天对话、语音消息、照片、图片和视频剪辑?参考本指南,祝您成功恢复微信数据。 关于微信数据恢复 “说真的&#xf…

容器化技术Docker与任务编排

Docker容器化 Docker简介 传统的Java项目部署需要自己进行打包,redis,nignx等中间件需要安装以及进行很多配置,稍微繁琐,而Docker使用了容器化的技术把这一过程封装为一条指令解决,而这取决于它的架构设计&#xff0c…

数值优化之函数高阶信息

本文ppt来自深蓝学院《机器人中的数值优化》 目录 1 函数高阶信息的介绍 2 函数高阶信息的计算 1 函数高阶信息的介绍 hessian矩阵是对称矩阵,最后一个公式是函数关于0的泰勒展开 负梯度是函数下降的最快方向 注意区分Hessian矩阵与Jacobian矩阵,Hess…

9_4、Java基本语法之System、Math、BigInteger与BigDecimal类的使用

一、System类的使用 1、System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。 该类位于java.lang包。 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实 例化该类。其内部的成员变量和成员方法都是static的&am…

【算法题解】7. 反转链表

文章目录题目解法一:迭代解题思路代码实现复杂度分析解法二:递归解题思路代码实现复杂度分析题目 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。来自:leetcode 解法一:迭代 解题思路 使用…

广州车展智能卷王:集度ROBO-01的取与舍

作者 | 德新 编辑 | 王博2022年的最后两天,今年最后一个A级车展在广州开幕。由于种种原因,不少车企匆忙备战或者干脆缺席,这届展会不如往届热闹。但也有憋了大招的选手,比如集度。 12月30日上午,集度在一场非常短的发布…

11-内部类and 12-集合初步

文章目录11-内部类链接外部类使用.this 和.new匿名内部类12-集合初步思考1&#xff0c;List<Apple> apples new ArrayList<>();思考2&#xff0c;如何初始化一个collection思考3&#xff0c;Pet关键字思考4&#xff0c;关于ListIterator这个双向迭代器思考5&#…

保护性暂停设计模式

目录 保护性暂停设计模式 获取结果 产生结果 总代码实现 测试 增加超时效果的Guarded suspension get(long timeout) 测试 保护性暂停设计模式 Guarded Suspension 即 保护性暂停; 是一种等待唤醒机制的一种规范 ,也可以理解为使用中设计模式,Java的API很多都按照保护性…

【免费开放源码】审批类小程序项目实战(活动审批端)

第一节&#xff1a;什么构成了微信小程序、创建一个自己的小程序 第二节&#xff1a;微信开发者工具使用教程 第三节&#xff1a;深入了解并掌握小程序核心组件 第四节&#xff1a;初始化云函数和数据库 第五节&#xff1a;云数据库的增删改查 第六节&#xff1a;项目大纲以及制…

系分 - 面向对象的方法【概念】

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 系分 - &#xff08;概念&#xff09;面向对象的方法 面向对象的方法&#xff08;OO&#xff0c;Object-Oriented&#xff09;是一种基于对象模型的程序设计方法&#xff0c;包括面向对象的分析&#xff08;OOA&a…

【iOS】内存管理

文章目录前言理解引用计数引用计数原理属性存取方法中的内存管理自动释放池保留环以ARC简化引用计数使用ARC时必须遵守的命名规则变量的内存管理语义ARC如何清理实例变量覆写内存管理的方法在dealloc方法中只释放应用并解除监听前言 内存管理&#xff1a; 在Objective-C这样的…

Windows平台下的内存泄漏检测

Windows平台下的内存泄漏检测一、使用_CrtDumpMemoryLeaks定位内存泄露添加对应的头文件转储内存泄漏信息程序任意点退出指定调试信息输出二、定位具体内存泄露位置内存快照转储内存快照比较内存快照完整例子三、使用WinDbg定位获取堆信息查看指定堆的使用情况获取地址信息获取…

【Docker】初级篇

【Docker】初级篇&#xff08;一&#xff09;Docker简介【1】docker是什么【2】容器与虚拟机比较【3】能干嘛【4】去哪下&#xff08;二&#xff09;Docker安装【1】前提说明【2】Docker的基本组成【3】安装步骤&#xff08;1&#xff09;确定是CentOS7及以上版本&#xff08;2…

抽烟打电话行为识别系统 yolo

抽烟打电话行为识别系统通过yolo深度学习框架模型&#xff0c;对现场画面区域进行7*24小时实时监测&#xff0c;发现抽烟打电话等违规行为立即抓拍存档预警。YOLOv5是一种单阶段目标检测算法&#xff0c;该算法在YOLOv4的基础上添加了一些新的改进思路&#xff0c;使其速度与精…