2024收尾工作

目录

开场白

栈与队列

LeetCode232. 用栈实现队列

LeetCode225. 用队列实现栈

LeetCode102. 二叉树的层序遍历

LeetCode103. 二叉树的锯齿形层序遍历

堆(优先级队列)

堆排序

LeetCode215. 数组中的第 k 个最大元素

总结


开场白

今天是除夕,在此首先祝各位读者朋友们新年快乐!愿你在新的一年里,心怀希望,勇敢追梦,愿每一天都充满温暖与惊喜,愿所有的努力都能开花结果,所有的期待都能如愿以偿。

从寒假开始,为了准备面试,我在我的个人CSDN账号陆续发布了Java实现数据结构与算法的相关文章,意在从我个人角度深度理解数据结构。将相关文章分享分布后,很多读者通过私信方式与我交流讨论,每次看到大家的留言,无论是鼓励、建议,还是探讨问题,都让我倍感温暖,也让我更加坚定了继续分享知识的决心。各位读者的支持是我持续创作的最大动力。

言归正传,我们今天简要分析前几篇文章中涉及到的数据结构在 LeetCode 中的应用。很多数据结构在实际问题中是结合着使用的,例如二叉树的层序遍历需要使用到队列作为辅助,因为层序遍历实际上是一种广度优先搜索(Breadth First Search,BFS,二叉树的前中后序遍历的迭代方式的实现需要使用到栈,即使在递归实现中我们使用三行代码可以实现需求,但是递归函数底层使用了函数调用栈,如果使用迭代方式,我们就需要使用栈来模拟函数调用栈的行为。

栈与队列

LeetCode232. 用栈实现队列

232. 用栈实现队列 - 力扣(LeetCode)

本题要求使用两个栈来模拟一个队列,此队列应该支持常见的入队、出队、获取队首元素以及判空几个常见操作。

这个题让我的思绪回到了大一考学院实验室的时候,当时过了笔试环节,面试环节一个学长很突然的问了我这个问题——“如何用两个栈模拟一个队列”,当时一紧张,说成了两栈共享空间,学长说 “不好意思,我没太明白你的意思”。

实际上只要了解了队列和栈的特性,解决这个问题就很容易了。我们需要定义两个栈,分别是输入栈输出栈,前者用于接收入队元素,后者用于处理获取队首元素和出队操作。假设我们连续向队列中入队元素,我们直接将这些元素按顺序压入输入栈,此时输入栈就保存了所有入队元素,栈顶元素就是队尾元素,如果我们需要执行出队获取队首元素操作,我们需要将输入栈的所有元素按顺序出栈,然后重新压入到输出栈,那么此时输出栈的栈顶元素就变成了模拟队列的队首元素,执行出队操作就是将输出栈的栈顶元素出栈,获取队首元素就是获取输出栈的栈顶元素。如果后续还需要入队元素,直接将元素压入输入栈,如果需要获取队首元素或者出队,先判断一下输出栈是否为空,如果输出栈为空,就需要执行相同的操作将输入栈的元素按顺序入栈到输出栈,此时栈顶元素就是队首元素。实现代码如下。

class MyQueue {private final ArrayStack<Integer> inputStack;private final ArrayStack<Integer> outputStack;private int size;public MyQueue() {inputStack = new ArrayStack<>(101);outputStack = new ArrayStack<>(101);size = 0;}public void push(int x) {inputStack.push(x);size++;}public int pop() {if (outputStack.isEmpty())while (!inputStack.isEmpty())outputStack.push(inputStack.pop());size--;return outputStack.pop();}public int peek() {if (outputStack.isEmpty())while (!inputStack.isEmpty())outputStack.push(inputStack.pop());return outputStack.peek();}public boolean empty() {return size == 0;}
}

在以上实现中,我们在类 MyQueue 中定义了一个用于表示模拟队列存储元素数量的变量 size,通过此方式在后续实现 empty 方法时就很容易,直接判断 size 是否为 0 即可,我们也可以通过判断两栈是否同时为空得出队列是否为空。

需要提前指出的是,我们这里使用了顺序栈,在构造顺序栈时需要传入期望规模参数 capacity,本题已经说明,入队出队操作不会超过 100 次,我们直接规定顺序栈的期望规模为 101,这样就不需要执行判满判空等操作。再次提醒,提交代码时需要将自定义的顺序栈类 ArrayStack 同时提交。当然,我们也可以使用 java.util.LinkedList 类来实现栈。

LeetCode225. 用队列实现栈

225. 用队列实现栈 - 力扣(LeetCode)

本题需要使用两个队列模拟栈的操作,模拟栈需要支持普通栈的入栈、出栈、获取栈顶元素和判空操作。我们知道,队列是一种先入先出FIFO数据结构,而栈是一种后入先出LIFO数据结构,如果按照顺序将元素入队,那么队首一定是模拟栈的栈底元素,而队尾一定是模拟栈的栈顶元素。所以使用队列模拟栈的时候,我们可以直接让元素序列入队到第一个队列,在出栈或获取栈顶元素时,让队尾之前的所有元素出队,并让这些元素按刚才出队的顺序入队到另一个队列,这个队列的作用就是暂存模拟栈栈顶元素的所有后继元素,如果是出栈操作,将第一个队列剩余的最后一个元素出队并用变量接收返回即可,然后将第二个队列的所有元素出队,让这些元素按照顺序重新入队到第一个队列;如果只是单纯获取栈顶元素,我们在拿到第一个队列剩余的最后一个元素后,第二个队列的所有元素出队,让这些元素按照出队顺序重新入队到第一个队列。方便起见,我们可以自定义一个用于表示模拟栈中存储元素数量的变量 size,所以实现判空操作就很简单了,直接判断 size 是否为 0 即可。代码如下。

class MyStack {private final ArrayQueue<Integer> queue1;private final ArrayQueue<Integer> queue2;private int size;public MyStack() {queue1 = new ArrayQueue<>(101);queue2 = new ArrayQueue<>(101);size = 0;}public void push(int x) {queue1.offer(x);size++;}public int pop() {int temp = size;while (temp -- != 1)queue2.offer(queue1.pull());Integer ans = queue1.pull();while (!queue2.isEmpty())queue1.offer(queue2.pull());size--;return ans;}public int top() {int temp = size;while (temp -- != 1)queue2.offer(queue1.pull());Integer ans = queue1.pull();while (!queue2.isEmpty())queue1.offer(queue2.pull());queue1.offer(ans);return ans;}public boolean empty() {return size == 0;}
}

当然本题的进阶实现是直接使用一个队列模拟栈操作,可以优化的地方就是元素转移阶段,我们可以不需要定义另一个队列暂存这些元素,而是直接让这些元素重新入队即可。整个调整过程就是,将队尾之前的所有元素出队,每个元素出队后又立刻入队到队尾,整个操作结束后,队首元素就是模拟栈的栈顶元素。代码如下。

class MyStack {private final ArrayQueue<Integer> queue;private int size = 0;public MyStack() {queue = new ArrayQueue<>(101);size = 0;}public void push(int x) {queue.offer(x);size++;}public int pop() {int temp = size - 1;while (temp -- != 0)queue.offer(queue.pull());size--;return queue.pull();}public int top() {int temp = size - 1;while (temp -- != 0)queue.offer(queue.pull());Integer ans = queue.pull();queue.offer(ans);return ans;}public boolean empty() {return size == 0;}
}

我们通过上面两个代码片段不难发现,出栈和获取栈顶元素这两个操作有重复代码,我们不妨将共有的代码提取到入栈操作处,即在入栈时就进行队列中元素的调整,让队尾元素来到队首,通过这种实现方式,在后续的出栈和获取栈顶元素时,我们直接对队首元素进行操作即可。代码如下。

class MyStack {private final ArrayQueue<Integer> queue1;private final ArrayQueue<Integer> queue2;private int size;public MyStack() {queue1 = new ArrayQueue<>(101);queue2 = new ArrayQueue<>(101);size = 0;}public void push(int x) {queue1.offer(x);int temp = size;while (temp -- != 0)queue2.offer(queue1.pull());while (!queue2.isEmpty())queue1.offer(queue2.pull());size++;}public int pop() {size--;return queue1.pull();}public int top() {return queue1.peek();}public boolean empty() {return size == 0;}
}
class MyStack {private final ArrayQueue<Integer> queue;private int size = 0;public MyStack() {queue = new ArrayQueue<>(101);size = 0;}public void push(int x) {queue.offer(x);int temp = size;while (temp -- != 0)queue.offer(queue.pull());size++;}public int pop() {size--;return queue.pull();}public int top() {return queue.peek();}public boolean empty() {return size == 0;}
}

LeetCode102. 二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

二叉树的层序遍历遵循着广度优先搜索的不断延伸思想。具体来说,我们需要使用一个队列用来暂存需要遍历的结点,让根节点入队,每次取出队列中的队首结点并将其的左右孩子存入队列,不断进行此操作,就可以让队列中存储着当前层的下一层结点,实际上,队列的出队顺序就是此二叉树的层序遍历序列。

在具体实现中,由于队列只有在遍历到叶子结点后才会为空,我们需要专门定义一个变量用来存储当前层的结点数,每次取出队列元素都是基于这个变量的,否则会取出下一层的结点。

    public List<List<Integer>> levelOrder(TreeNode root) {if (root == null)return new ArrayList<>();List<List<Integer>> ans = new ArrayList<>();ArrayQueue<TreeNode> queue = new ArrayQueue<>(2001);queue.offer(root);int levelNum = 1;while (!queue.isEmpty()) {List<Integer> list = new ArrayList<>();int cnt = 0;for (int i = 1; i <= levelNum; i++) {TreeNode node = queue.pull();list.add(node.val);if (node.left != null) {queue.offer(node.left);cnt++;}if (node.right != null) {queue.offer(node.right);cnt++;}}levelNum = cnt;ans.add(list);}return ans;}

LeetCode103. 二叉树的锯齿形层序遍历

103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)

本题与上题类似,我们认为根节点所处层为第一层,通过题目要求我们不难发现,当层序号为奇数时,我们需要从左到右遍历,反之,如果层序号为偶数时,我们需要从右到左遍历,这样就可以实现锯齿形遍历。我们在每层接收结果的时候需要使用一个双端队列,若当前层的层序号为奇数,我们将队列的队首结点数据从尾部插入到双端队列,否则从头部插入双端队列,后续的操作与上一题是一致的,不再赘述。

为了方便实现,我们使用了 java.util.LinkedList 进行实现,代码如下。

    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {if (root == null)return new ArrayList<>();List<List<Integer>> ans = new ArrayList<>();LinkedList<TreeNode> queue = new LinkedList<>();queue.offer(root);int levelNum = 1;boolean isOdd = true;while (!queue.isEmpty()) {int cnt = 0;LinkedList<Integer> level = new LinkedList<>();for (int i = 1; i <= levelNum; i++) {TreeNode node = queue.poll();if (node != null) {if (isOdd)level.offerLast(node.val);elselevel.offerFirst(node.val);if (node.left != null) {queue.offer(node.left);cnt++;}if (node.right != null) {queue.offer(node.right);cnt++;}}}levelNum = cnt;isOdd = !isOdd;ans.add(level);}return ans;}

堆(优先级队列)

堆排序

堆排序是基于堆的一种排序方法,假设我们使用大顶堆进行堆内元素的排序,执行建堆操作后,堆顶元素是所有元素的最大值,我们可以选择将堆顶元素与堆的最后一个元素进行交换,然后让堆的 size 变量减 1,这样就表示我们不再维护最大值元素,此时将交换到堆顶的元素执行一次下潜操作,新的堆顶元素就是剩下的 size-1 个元素中的最大值。不断执行此操作,直到堆中维护的元素数量为 1 为止。

    public static void main(String[] args) {MaxHeap heap = new MaxHeap(new int[]{1, 34, 54, 13, 25, 6, 9, 17});System.out.println(Arrays.toString(heap.array));while (heap.size > 1) {heap.swap(0, heap.size - 1);heap.size --;heap.down(0);}System.out.println(Arrays.toString(heap.array));}

LeetCode215. 数组中的第 k 个最大元素

215. 数组中的第K个最大元素 - 力扣(LeetCode)

本题给定一个数组和一个整型变量 k,届时需要返回数组中第 k 个最大的元素。初见此题,第一想法是直接对数组进行排序,然后返回指定位置元素即可,但是这种方式并没有考虑到数组中存在重复元素的情况。

我们可以直接使用一个大顶堆来维护所有元素,然后执行 k-1 次出队操作即可。

    public int findKthLargest(int[] nums, int k) {MaxHeap heap = new MaxHeap(nums);while (k -- != 0)heap.pull();return heap.peek();}

实际上,我们也并不需要维护所有的元素,直接定义一个大小为 k 的小顶堆,先将前 k 个元素存入堆中,然后继续遍历数组,若当前遍历的元素大于堆顶元素,则执行一次替换堆顶元素操作,最终堆顶元素就是第 k 个最大元素。

    public int findKthLargest(int[] nums, int k) {MinHeap heap = new MinHeap(k);for (int i = 0; i < k; i ++)heap.offer(nums[i]);for (int i = k; i < nums.length; i ++) {if (nums[i] > heap.peek())heap.replace(nums[i]);}return heap.peek();}

总结

本文写的比较仓促,因为我要过年了。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

纯css实现div宽度可调整

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>纯css实现div尺寸可调整</title><style…

浅谈Linux的发展

目录 1.Linux背景 1.1 发展史 UNIX发展的历史 1.2开源 1.3官网 1.4.企业应用现状 1.5.发行版本 1.6 os概念&#xff0c;定位 1.Linux背景 1.1 发展史 学习Linux系统编程&#xff0c;你可能要问Linux从哪里来&#xff1f;它是怎么发展的&#xff1f;在这里简要介绍Linux的发展史…

四层网络模型

互联网由终端主机、链路和路由器组成&#xff0c;数据通过逐跳的方式&#xff0c;依次经过每条链路进行传输。 网络层的工作是将数据包从源端到目的端&#xff0c;跨越整个互联网。 网络层的数据包称为数据报。网络将数据报交给链路层&#xff0c;指示它通过第一条链路发送数据…

世上本没有路,只有“场”et“Bravo”

楔子&#xff1a;电气本科“工程电磁场”电气研究生课程“高等电磁场分析”和“电磁兼容”自学”天线“、“通信原理”、“射频电路”、“微波理论”等课程 文章目录 前言零、学习历程一、Maxwells equations1.James Clerk Maxwell2.自由空间中传播的电磁波3.边界条件和有限时域…

python学opencv|读取图像(四十六)使用cv2.bitwise_or()函数实现图像按位或运算

【0】基础定义 按位与运算&#xff1a;全1取1&#xff0c;其余取0。按位或运算&#xff1a;全0取0&#xff0c;其余取1。 【1】引言 前序学习进程中&#xff0c;已经对图像按位与计算进行了详细探究&#xff0c;相关文章链接如下&#xff1a; python学opencv|读取图像&…

如何把obsidian的md文档导出成图片,并加上文档属性

上篇关于这个插件PKMer_Obsidian 插件&#xff1a;Export Image plugin 一键将笔记转换为图片分享的文章 如何把obsidian的md文档导出成图片&#xff0c;并加上水印-CSDN博客 如何导出图片的时候让文档属性也显示出来&#xff0c;啊啊&#xff0c;这个功能找了一晚上&#xf…

MATLAB算法实战应用案例精讲-【数模应用】方向梯度直方图(HOG)(附python代码实现)

目录 前言 算法原理 特征描述 什么是方向梯度直方图? 算法思想: 实现方法: 性能提高: HOG特征提取 直方图阈值化 直方图均衡化 算法步骤: 算法流程 1. 图像预处理 2. 计算图像梯度 3. 计算梯度直方图 4. 图像HOG特征向量 直方图反向投影 其它类型图像直…

CycleGAN模型解读(附源码+论文)

CycleGAN 论文链接&#xff1a;Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks 官方链接&#xff1a;pytorch-CycleGAN-and-pix2pix 老规矩&#xff0c;先看看效果 总体流程 先简单过一遍流程&#xff0c;细节在代码里说。CycleGAN有…

ue5 GAS制作一个技能,技能冷却,给剑添加碰撞预设,打击敌人

总结&#xff1a; 新建文件夹 ability 取名BP_BaseAbility 新建一个技能GAB_Melee 上面技能GAB_Melee和技能基类BP_BaseAbility 进入技能GAB_Melee&#xff0c;添加打印火云掌 给这个技能添加标签 点这个号 这样命名&#xff0c;小心这个点&#xff08;.&#xff09…

工作总结:git篇

文章目录 前言基础Gerrit1.克隆2.新建本地分支和checkout3.添加到暂存区新增文件到暂存区修改已经添加到暂存区的文件取消添加到暂存区的文件 4.提交到本地仓库在不重复提交的情况下&#xff0c;修改本次提交 5.提交到远程仓库6.评审其他辅助命令 前言 目前也算是工作一段时间…

ESP32 I2S音频总线学习笔记(二):I2S读取INMP441音频数据

简介 在这个系列的上一篇文章中&#xff0c;我们介绍了ESP32 I2S音频总线的相关知识&#xff0c;简要了解了什么是I2S总线、它的通信格式&#xff0c;以及相关的底层API函数。没有看过上篇文章的可以点击文章进行回顾&#xff1a; ESP32 I2S音频总线学习笔记&#xff08;一&a…

(学习总结21)C++11 异常与智能指针

C11 异常与智能指针 异常异常的概念异常的抛出和捕获栈展开查找匹配的处理代码异常重新抛出异常安全问题异常规范标准库的异常 智能指针RAII 和智能指针的设计思路智能指针的使用场景分析C标准库智能指针的使用weak_ptr 和 shared_ptr循环引用weak_ptrshared_ptr 循环引用问题 …

智能调度体系与自动驾驶技术优化运输配送效率的研究——兼论开源AI智能名片2+1链动模式S2B2C商城小程序的应用潜力

摘要&#xff1a;随着全球化和数字化进程的加速&#xff0c;消费者需求日益呈现出碎片化和个性化的趋势&#xff0c;这对物流运输行业提出了前所未有的挑战。传统的物流调度体系与调度方式已难以满足当前复杂多变的物流需求&#xff0c;因此&#xff0c;物流企业必须积极引入大…

AndroidCompose Navigation导航精通1-基本页面导航与ViewPager

文章目录 前言基本页面导航库依赖导航核心部件简单NavHost实现ViewPagerPager切换逻辑图阐述Pager导航实战前言 在当今的移动应用开发中,导航是用户与应用交互的核心环节。随着 Android Compose 的兴起,它为开发者提供了一种全新的、声明式的方式来构建用户界面,同时也带来…

noteboolm 使用笔记

今天心血来潮&#xff0c;想要体验下AInotebook&#xff0c;看看最新的软件能够做到什么程度。 于是来到了notebooklm&#xff0c;这是一个google推出的AI笔记本的网站&#xff0c;我想知道我们能在上面做一些怎么样有趣的事情&#xff01; 网址&#xff1a;https://notebookl…

JAVA 接口、抽象类的关系和用处 详细解析

接口 - Java教程 - 廖雪峰的官方网站 一个 抽象类 如果实现了一个接口&#xff0c;可以只选择实现接口中的 部分方法&#xff08;所有的方法都要有&#xff0c;可以一部分已经写具体&#xff0c;另一部分继续保留抽象&#xff09;&#xff0c;原因在于&#xff1a; 抽象类本身…

ReactNative react-devtools 夜神模拟器连调

目录 一、安装react-devtools 二、在package.json中配置启动项 三、联动 一、安装react-devtools yarn add react-devtools5.3.1 -D 这里选择5.3.1版本&#xff0c;因为高版本可能与夜神模拟器无法联动&#xff0c;导致部分功能无法正常使用。 二、在package.json中配置启…

关于使用Mybatis-plus的TableNameHandler动态表名处理器实现分表业务的详细介绍

引言 随着互联网应用的快速发展&#xff0c;数据量呈爆炸式增长。传统的单表设计在面对海量数据时显得力不从心&#xff0c;容易出现性能瓶颈、查询效率低下等问题。为了提高数据库的扩展性和响应速度&#xff0c;分表&#xff08;Sharding&#xff09;成为了一种常见的解决方案…

【开源免费】基于Vue和SpringBoot的在线文档管理系统(附论文)

本文项目编号 T 038 &#xff0c;文末自助获取源码 \color{red}{T038&#xff0c;文末自助获取源码} T038&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

智慧园区系统分类及其在提升企业管理效率中的创新应用探讨

内容概要 智慧园区的概念已经逐渐深入人心&#xff0c;成为现代城市发展中不可或缺的一部分。随着信息技术的飞速发展和数字化转型的不断推进&#xff0c;一系列智慧园区管理系统应运而生。这些系统不仅帮助企业提高了管理效率&#xff0c;还在多个方面激发了创新。 首先&…