左神算法基础巩固--2

文章目录

  • 稳定性
    • 选择排序
    • 冒泡排序
    • 插入排序
    • 归并排序
    • 快速排序
    • 堆排序
  • 哈希表
  • 链表
    • 解题


稳定性

稳定性是指算法在排序过程中保持相等元素之间相对顺序的特性。具体来说,如果一个排序算法是稳定的,那么对于任意两个相等的元素,在排序前它们的相对顺序与排序后它们的相对顺序是相同的。

稳定性对于常见进行数字排序的情况没有意义但对于复杂类型的排序来说是一个特别重要的一件事。
举例说明:我们对于一个班级的所有学生先按照年龄进行排序,再按照成绩进行排序,如果排序算法具有稳定性的话便会得到一个如果成绩相同则年龄较小的会排在前面的对象数组。这就是算法稳定性的运用。


前文我们提到的那几种排序算法中并不是所有的算法都具有稳定性的

选择排序

选择选择排序是不具有稳定性的,根据选择排序的算法思路来说,选择排序会先在数组中找到小值并将其与对应位置的数进行交换,故如果出现以下这种情况,那么其就不具有稳定性
在这里插入图片描述

冒泡排序

冒泡排序是否具有稳定性得看你在什么时候交换位置,如果相等你不交换二者的位置,那么冒泡排序便是具有稳定性的。以以下数组为例
由冒泡排序的算法思想得在第一次遍历后0位置上的6会在5位置上只要原0位置上的数不与5位置上的6交换则此时算法是具有稳定性的
在这里插入图片描述

插入排序

插入排序和冒泡排序类似,只要我们设定正确的在两数相同的情况下不交换两数的位置,那么此时该算法就是具有稳定性的以以下数组为例
在这里插入图片描述
该数组在0-0范围内是有序的,在0-1范围内需要交换位置,交换后数组为2 3 2 … ,在0-2范围内由于2位置上的数与0位置上的数相同只要我们不交换二者的位置,此时该算法具有稳定性

归并排序

归并排序的稳定性取决于其merge的实现,只要我们在merge的时候实现两数相等时先拷贝左侧的数便能实现稳定性
以以下数组为例
在这里插入图片描述
在这个数组中1与1相等,只要我们在将数组整理时,先拷贝左侧的1便能实现该算法的稳定性

快速排序

快速排序则是做不到稳定性的,主要原因在于快速排序的partition过程,在partition过程中会将从前往后的第一个小于基准值的数与左界限后的第一个数交换位置,这一步骤便破坏了稳定性。
以以下数组为例
在这里插入图片描述
在该数组中找到第一个小于基准数的数为3此时他要与左界限后的第一个数6交换位置此时便破坏了稳定性。

堆排序

堆排序也做不到稳定性,堆排序是以维护自身的堆结构来实现排序的,因此其在设计之初便完全没有考虑稳定性
以以下数组为例
在这里插入图片描述
在加入这个6后堆排序会去维持其堆结构即将6挂载在第一个4的后面并进行比较由于6比4大,所以6会与第一个4交换位置,此时便破坏了稳定性

下面是算法稳定性的总结
在这里插入图片描述
一些常见的坑
在这里插入图片描述
注意:这些算法并不是相互分离不可结合的,以选择排序和快速排序为例,虽然选择排序的时间复杂度为n^2快速排序的时间复杂度为nlogn 但是在n比较小的情况下,其实选择排序的所需时间是比快速排序要少的,因此可以进行判断如果n较小便使用选择排序,如果n较大便使用快速排序。
在这里插入图片描述

哈希表

在这里插入图片描述
在这里插入图片描述
常用的操作
在这里插入图片描述
在这里插入图片描述

链表

在这里插入图片描述

解题

在这里插入图片描述
笔试思路:先用快慢指针遍历这个链表得到链表的前半部分和后半部分,将这个链表的前半部分加入到一个栈中,在加入后,遍历后半个链表,每遍历一个都与使栈弹出的数进行比较如果两数相等便接着比较,如果栈或链表为空了则说明该链表是回文结构,如果出现不相等的情况则说明该链表不是回文结构。

import java.util.Stack;class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// 使用快慢指针找到中间节点ListNode slow = head;ListNode fast = head;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}// 将前半部分链表的节点值压入栈中Stack<Integer> stack = new Stack<>();ListNode temp = head;while (temp != slow) {stack.push(temp.val);temp = temp.next;}// 如果链表长度为奇数,跳过中间节点if (fast != null) {slow = slow.next;}// 比较后半部分链表的节点值与栈中的值while (slow != null) {if (slow.val != stack.pop()) {return false;}slow = slow.next;}return true;}public static void main(String[] args) {Solution solution = new Solution();ListNode head = new ListNode(1);head.next = new ListNode(2);head.next.next = new ListNode(2);head.next.next.next = new ListNode(1);boolean isPalindrome = solution.isPalindrome(head);System.out.println("链表是否为回文结构: " + isPalindrome);}
}

面试思路: 先用快慢指针,使当快指针走到尽头时,慢指针指向中点位置,然后将链表的后半部分反转,并使前半部分和后半部分均向中点靠拢并比较,如果两数相等便接着比较,直到遍历前半部分的指针和遍历后半部分的指针指向中点,就先将后半部分复原后返回true,如果不等就复原后返回false

class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// 使用快慢指针找到中间节点ListNode slow = head;ListNode fast = head;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}// 反转链表的后半部分ListNode prev = null;ListNode curr = slow;while (curr != null) {ListNode nextTemp = curr.next;curr.next = prev;prev = curr;curr = nextTemp;}// 比较前半部分和反转后的后半部分ListNode left = head;ListNode right = prev;boolean isPalindrome = true;while (right != null) {if (left.val != right.val) {isPalindrome = false;break;}left = left.next;right = right.next;}// 恢复链表的后半部分curr = prev;prev = null;while (curr != null) {ListNode nextTemp = curr.next;curr.next = prev;prev = curr;curr = nextTemp;}return isPalindrome;}public static void main(String[] args) {Solution solution = new Solution();ListNode head = new ListNode(1);head.next = new ListNode(2);head.next.next = new ListNode(2);head.next.next.next = new ListNode(1);boolean isPalindrome = solution.isPalindrome(head);System.out.println("链表是否为回文结构: " + isPalindrome);}
}

在这里插入图片描述
笔试思路:遍历链表,将链表的值加入到数组中,对数组进行partition,之后再将用得到的数组构建出一个链表。

class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {public ListNode partition(ListNode head, int pivot) {if (head == null) return null;// 初始化三个指针,分别指向小于、等于、大于pivot的链表头部ListNode beforeSmaller = new ListNode(0);ListNode beforeEqual = new ListNode(0);ListNode beforeGreater = new ListNode(0);// 当前节点ListNode current = head;// 尾指针ListNode tailSmaller = beforeSmaller;ListNode tailEqual = beforeEqual;ListNode tailGreater = beforeGreater;while (current != null) {if (current.val < pivot) {tailSmaller.next = current;tailSmaller = tailSmaller.next;} else if (current.val == pivot) {tailEqual.next = current;tailEqual = tailEqual.next;} else {tailGreater.next = current;tailGreater = tailGreater.next;}current = current.next;}// 连接三个链表tailSmaller.next = beforeEqual.next;tailEqual.next = beforeGreater.next;tailGreater.next = null;// 返回新链表的头节点return beforeSmaller.next;}public static void main(String[] args) {Solution solution = new Solution();ListNode head = new ListNode(4);head.next = new ListNode(3);head.next.next = new ListNode(2);head.next.next.next = new ListNode(-1);head.next.next.next.next = new ListNode(2);head.next.next.next.next.next = new ListNode(3);head.next.next.next.next.next.next = new ListNode(5);int pivot = 2;ListNode newHead = solution.partition(head, pivot);// 打印结果,用于验证while (newHead != null) {System.out.print(newHead.val + " ");newHead = newHead.next;}}
}

面试思路:遍历这个链表并根据他们的情况将链表节点加入到新建的三个链表中,遍历完成后,对链表进行拼接便能得到完整的解

class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}public class Solution {public ListNode partition(ListNode head, int pivot) {// 虚拟头节点,方便操作ListNode dummyBeforeSmaller = new ListNode(0);ListNode dummyBeforeEqual = new ListNode(0);ListNode dummyBeforeGreater = new ListNode(0);ListNode smaller = dummyBeforeSmaller;ListNode equal = dummyBeforeEqual;ListNode greater = dummyBeforeGreater;while (head != null) {if (head.val < pivot) {smaller.next = head;smaller = smaller.next;} else if (head.val == pivot) {equal.next = head;equal = equal.next;} else {greater.next = head;greater = greater.next;}head = head.next;}// 连接三个链表smaller.next = dummyBeforeEqual.next;equal.next = dummyBeforeGreater.next;greater.next = null;return dummyBeforeSmaller.next;}public static void main(String[] args) {Solution solution = new Solution();ListNode head = new ListNode(4);head.next = new ListNode(3);head.next.next = new ListNode(2);head.next.next.next = new ListNode(-1);head.next.next.next.next = new ListNode(2);head.next.next.next.next.next = new ListNode(3);head.next.next.next.next.next.next = new ListNode(5);int pivot = 2;ListNode newHead = solution.partition(head, pivot);// 打印结果,用于验证while (newHead != null) {System.out.print(newHead.val + " ");newHead = newHead.next;}}
}

在这里插入图片描述

在这里插入图片描述
笔试思路:使用hash表将原节点与原节点对应的克隆节点加入到hash表中,其中hash表的key为原节点,value为克隆节点,之后再从原链表中找到对应的next指针对应的节点,将其从hash表中找到对应的克隆节点并赋值给源节点对应克隆节点的next指针,rand指针也是一样不断重复直到所有节点都判断过

class Node {int value;Node next;Node rand;Node(int val) {value = val;next = null;rand = null;}
}public class Solution {public Node copyRandomList(Node head) {if (head == null) return null;// Step 1: 创建哈希表存储原始节点和克隆节点的映射Map<Node, Node> map = new HashMap<>();// Step 2: 遍历原始链表,克隆每个节点并存储在哈希表中Node current = head;while (current != null) {map.put(current, new Node(current.value));current = current.next;}// Step 3: 复制next和rand指针current = head;while (current != null) {Node clone = map.get(current);clone.next = map.getOrDefault(current.next, null);clone.rand = map.getOrDefault(current.rand, null);current = current.next;}// Step 4: 返回新链表的头节点return map.get(head);}
}

面试思想:首先在原链表上克隆每个节点并使原节点的的next指针指向克隆节点,克隆节点的next指针指向原节点的next指针所指节点,之后再遍历原链表节点的next指针和rand指针并在克隆节点上对应指向

class Node {int value;Node next;Node rand;Node(int val) {value = val;next = null;rand = null;}
}public class Solution {public Node copyRandomList(Node head) {if (head == null) return null;// 第一步:克隆节点并建立连接Node current = head;while (current != null) {Node clone = new Node(current.value);clone.next = current.next;current.next = clone;current = clone.next;}// 第二步:复制 rand 指针current = head;while (current != null) {Node clone = current.next;if (current.rand != null) {clone.rand = current.rand.next;}current = clone.next;}// 第三步:分离链表并返回新链表的头节点Node newHead = head.next; // 新链表的头节点是原链表第一个节点的克隆节点current = head;while (current != null) {Node clone = current.next;current.next = clone.next; // 断开原始节点和克隆节点的连接if (clone.next != null) {clone.next = clone.next.next; // 跳过原始节点,连接到下一个克隆节点}current = current.next;}return newHead;}
}

在这里插入图片描述
在这里插入图片描述
这道题需要直到环中快慢指针的问题,需要明确的是用两个指针其中快指针一次走两步,慢指针一次走一步遍历这个链表当这个链表存在环时,他两一定在环上相遇,同时再将快指针指向第一个节点,再次同时出发,他两一定在入环处相遇。
这道题要分为几种情况进行讨论:
1.两个链表不相交且无环
在这里插入图片描述
在这种情况下,我们先用快慢指针知道是否有环后,遍历这两个链表,并记录下每个链表的最后的节点和这个数组的长度,当这两个链表不相交且无环时,这两个链表所对应的最后的节点是不同的。

2.无环但相交
在这里插入图片描述

这种情况的判定方法,第一种情况类似我们先用快慢指针知道是否有环后,遍历这两个链表,并记录下每个链表的最后的节点和这两个链表的长度,并作差值n,当这两个链表相交但无环时,这两个链表所对应的最后的节点是相同的,同时记录下较长的链表让用指针指向其头部并先走n步,之后用指针指向另一链表的头部,两者同时走,两指针相等时便是两指针的相交节点。

之后便是有环结构了,这种情况需要统一分析
在这里插入图片描述

这三种情况的判定条件是入环节点loop1和loop2均不为空判断loop1是否等于loop2,如果相等则为情况2,否则让loop1继续往下走,如果loop1每遇见loop2就是第一种情况,如果遇见了就是情况3。

遇到情况1便可直接返回null,遇到情况二则与前面的无环链表相交类型,但以loop1为链表最后一个节点,遇到情况三则返回loop1或loop2


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

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

相关文章

UART串口数据分析

串口基础知识详细介绍&#xff1a; 该链接详细介绍了串并行、单双工、同异步、连接方式 https://blog.csdn.net/weixin_43386810/article/details/127156063 该文章将介绍串口数据的电平变化、波特率计算、脉宽计算以及数据传输量的计算。 捕获工具&#xff1a;逻辑分析仪&…

机器学习模型评估指标

模型的评估指标是衡量一个模型应用于对应任务的契合程度&#xff0c;常见的指标有&#xff1a; 准确率&#xff08;Accuracy&#xff09;: 正确预测的样本数占总样本数的比例。适用于类别分布均衡的数据集。 精确率&#xff08;Precision&#xff09;: 在所有被预测为正类的样…

面试题解,JVM中的“类加载”剖析

一、JVM类加载机制说一下 其中&#xff0c;从加载到初始化就是我们的类加载阶段&#xff0c;我们逐一来分析 加载 “加载 loading”是整个类加载&#xff08;class loading&#xff09;过程的一个阶段&#xff0c;加载阶段JVM需要完成以下 3 件事情&#xff1a; 1&#xff0…

腾讯云AI代码助手编程挑战赛-古诗词学习

一、作品介绍 在科技与文化深度交融的当下&#xff0c;“腾讯云 AI 代码助手编程挑战赛 - 每日古诗词” 宛如一颗璀璨的新星&#xff0c;闪耀登场。它绝非一场普通的赛事&#xff0c;而是一座连接编程智慧与古典诗词韵味的桥梁。 这项挑战赛以独特的视角&#xff0c;将每日古…

GelSight Mini视触觉传感器凝胶触头升级:增加40%耐用性,拓展机器人与触觉AI 应用边界

马萨诸塞州沃尔瑟姆-2025年1月6日-触觉智能技术领军企业Gelsight宣布&#xff0c;旗下Gelsight Mini视触觉传感器迎来凝胶触头的更新。经内部测试&#xff0c;新Gel凝胶触头耐用性提升40%&#xff0c;外观与触感与原凝胶触头保持一致。此次升级有效满足了客户在机器人应用中对设…

【C++入门】详解(上)

目录 &#x1f495;1.C中main函数内部———变量的访问顺序 &#x1f495;2.命名空间域namespace &#x1f495;3.命名空间域&#xff08;代码示例&#xff09;&#xff08;不要跳&#xff09; &#x1f495;4.多个命名空间域的内部重名 &#x1f495;5.命名空间域的展开 …

Ungoogled Chromium127 编译指南 MacOS篇(八)- 开始编译

1. 引言 完成了所有依赖包的安装后&#xff0c;我们终于来到了最关键的编译阶段。在开始编译之前&#xff0c;有一些重要的配置信息需要了解。本文将指导您完成整个编译过程。 2. 签名相关说明 虽然在我们的测试编译中不需要进行签名操作&#xff0c;但了解官方的签名要求仍…

使用uniapp 微信小程序一些好用的插件分享

总结一下自己在开发中遇见的一问题&#xff0c;通过引入组件可以快速的解决 1.zxz-uni-data-select 下拉框选择器(添加下拉框检索&#xff0c;多选功能&#xff0c;多选搜索功能&#xff0c;自定义 下拉框插件&#xff0c;使用这个的原因是因为 uniui uview 组件库下拉框太…

腾讯云AI代码助手编程挑战赛-有趣的冷知识分享

作品简介 有趣的冷知识这一编程主要用于对于小朋友的探索力的开发&#xff0c;让小朋友在一开始就对学习具有探索精神。在信息化时代下&#xff0c;会主动去学习自己认知以外的知识&#xff0c;同时丰富了眼界&#xff0c;开拓了新的知识。 技术架构 使用python语言的TK库…

使用 SQL 和表格数据进行问答和 RAG(7)—将表格数据(CSV 或 Excel 文件)加载到向量数据库(ChromaDB)中

将表格数据&#xff08;CSV 或 Excel 文件&#xff09;加载到向量数据库&#xff08;ChromaDB&#xff09;中。这里定义的类 PrepareVectorDBFromTabularData&#xff0c;它的主要功能是读取表格数据文件到DataFrame中、生成嵌入向量、并将这些数据存储在向量数据库的集合中&am…

攻防世界 wtf.sh-150

点进去&#xff0c;发现是一个类似于论坛的网站&#xff0c;并且对报错等做了处理 用御剑扫描一下 ​ 发现是php形式的文件&#xff0c;但点进去访问不了。看看wp&#xff0c;发现此题存在路径穿越漏洞&#xff0c;就是&#xff08;如果应用程序使用用户可控制的数据&#xff0…

【Spring】Redis缓存+ehcache

文章目录 基于Spring的RedisehcacheRedis 缓存配置Cacheable 注解CacheEvict 注解缓存配置 基于Spring的Redisehcache Redis 缓存配置 在项目中添加 Redis 的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot…

UE5 打包要点

------------------------- 1、需要环境 win sdk &#xff0c;大约3G VS&#xff0c;大约10G 不安装就无法打包&#xff0c;就是这么简单。 ----------------------- 2、打包设置 编译类型&#xff0c;开发、调试、发行 项目设置-地图和模式&#xff0c;默认地图 项目…

小程序textarea组件键盘弹起会遮挡住输入框

<textarea value"{{remark}}" input"handleInputRemark" ></textarea> 如下会有遮挡&#xff1a; 一行代码搞定 cursor-spacing160 修改后代码 <textarea value"{{remark}}" input"handleInputRemark" cursor-spacin…

git的rebase和merge的区别?

B分支从A分支拉出 1.git merge 处于A分支执行&#xff0c;git merge B分支:相当于将commit X、commit Y两次提交&#xff0c;作为了新的commit Z提交到了A分支上。能溯源它真正提交的信息。 2.git rebase 处于B分支&#xff0c;执行git rebase A分支&#xff0c;B分支那边复…

Cadence——virtuous生成的symbol其端口自动添加注释

新换的工艺库&#xff0c;环境变量也会发生变化&#xff0c;毕竟每一家PDK下的.cdsinit 和 .cdsenv文件不尽相同。 这次新换的PDK&#xff0c;搭建完Schcematic之后&#xff0c;再生成的Symbol&#xff0c;发现其pin口&#xff0c;也就是端口没有自动生成注释&#xff0c;我就…

CClinkIEfield Basic转Modbus TCP网关模块连接三菱FX5U PLC

捷米特JM-CCLKIE-TCP是自主研发的一款CCLINK IE FB从站功能的通讯网关。该产品主要功能是将各种 MODBUS-TCP 设备接入到 CCLINK IE FB网络中。 捷米特JM-CCLKIE-TCP网关连接到CCLINK IE FB总线中做为从站使用&#xff0c;连接到 MODBUS-TCP 总线中做为主站或从站使用。 为了打破…

《分布式光纤测温:解锁楼宇安全的 “高精度密码”》

在楼宇建筑中&#xff0c;因其内部空间庞大&#xff0c;各类电器设施众多&#xff0c;如何以一种既高效又稳定&#xff0c;兼具低成本与高覆盖特性的方式&#xff0c;为那些关键线路节点开展温度监测&#xff0c;是目前在安全监测领域一项重点研究项目&#xff0c;而无锡布里渊…

开关电源设计中的穿越频率选取

摘要 在开关电源设计之中&#xff0c;穿越频率是一个相当重要的指标。在文中从就开关电源设计中的环路反馈控制方法、环路增益作用进行了阐述。并在此基础上就穿越频率选取的限制条件进行分析&#xff0c;为开关电源设计中的穿越频率选取提供借鉴。 开关电源设计过程中&#x…

探索 INFINI Console:提升 Elasticsearch 管理效率的新利器

1、常见的 Elasticsearch 可视化客户端介绍 1.1 Head 插件 这是一个非常老牌的 Elasticsearch 可视化插件&#xff08;现在改名了 Multi Elasticsearch Heads&#xff09;&#xff0c;通常以 Chrome 插件或网页形式存在&#xff0c;用来查看集群的节点状态、索引元数据&#x…