算法记录——链表

2.链表

2.1判断是否是回文链表

1.方法一:利用栈反转链表

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public boolean isPalindrome(ListNode head) {Stack<ListNode> listNodes = new Stack<>();ListNode p = head;//利用栈反转链表,判断是否是回文链表while (p != null) {//将链表中所有元素入栈listNodes.push(p);p = p.next;}while (!listNodes.empty()) {if (listNodes.pop().val == head.val) {//head = head.next;} else {return false;}}return true;}
}

2.方法2:利用快慢指针

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public boolean isPalindrome(ListNode head) {//代表快指针,一次走两步ListNode fast = head;//代表慢指针,一次走一步ListNode slow = head;while (fast.next != null && fast.next.next != null) {fast = fast.next.next;slow = slow.next;}//退出循环时,如果链表节点是奇数个,快指针在尾节点,慢指针在中点。如果是偶数个,快指针还是在尾节点,慢指针在中点前一个。//把右半部分链表反转slow = reverseList(slow.next);while (slow != null) {if (head.val != slow.val) return false;//值不相同,直接返回falsehead = head.next;slow = slow.next;}return true;}//反转链表public static ListNode reverseList(ListNode head) {ListNode cur = head;ListNode pre = null;while (cur != null) {ListNode temp = cur.next;cur.next = pre;pre = cur;cur = temp;}return pre;}
}

2.2 模板题:反转链表

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseList(ListNode head) {//cur:用于遍历链表元素的指针,代表当前遍历到的节点,初始化当然为head了ListNode cur = head;//pre:代表当前cur节点,反转后应该指向的节点。因为cur初始在head,反转以后就是尾节点了指向null,所以pre初始化为nullListNode pre = null;while(cur != null){//当元素还没遍历完的时候//在cur指向pre前,用于保存cur.next,防止链表找不到了。ListNode temp = cur.next;//让当前节点cur,指向precur.next = pre;//让pre变为反转链表的最前面一个节点pre = cur;//让cur移动到原链表的头节点cur = temp;}// 注意:pre的含义还是反转链表的头节点!return pre;}
}

复杂度分析:
时间复杂度 O(N)O(N)O(N) : 遍历链表使用线性大小时间。
空间复杂度 O(1)O(1)O(1) : 变量 pre 和 cur 使用常数大小额外空间。

已经是最优的解法了,还有一种递归方法就不赘述了。

2.3 分割链表(将链表分为小于某个值,等于某个值,大于某个值)

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode partition(ListNode head, int x) {
if (head == null || head.next == null) return head;//代表小于目标值区域的头和尾ListNode h1 = null;ListNode t1 = null;//代表大于等于目标值的头和尾ListNode h2 = null;ListNode t2 = null;//用于保存head的下一个节点//注意:这里最后拼接好了以后,小于区域的头就是整个链表的新的头节点,因此,head可以作为遍历链表的指针。ListNode next = head.next;while (head != null) {//遍历next = head.next;head.next = null;if (head.val < x) {//如果当前节点的val小于目标值if (h1 == null) {//如果当前节点是小于区域的第一个节点h1 = head;t1 = head;} else {t1.next = head;t1 = head;}} else {if (h2 == null) {//如果当前节点是大于区域的第一个节点h2 = head;t2 = head;} else {//其他情况就把该节点尾插法插入链表中t2.next = head;t2 = head;}}head = next;}//进行小于区域链表和大于等于区域链表的拼接if (h2 == null) {//如果没有大于等于区域return h1;}if (h1 == null) {//如果没有小于区域return h2;}//如果两种区域都有,则让小于区域的尾指针指向大于等于区域的头指针t1.next = h2;return h1;}
}

2.4 随机链表的赋值

/*
// Definition for a Node.
class Node {int val;Node next;Node random;public Node(int val) {this.val = val;this.next = null;this.random = null;}
}
*/class Solution {public Node copyRandomList(Node head) {//创建一个map,key为老链表的节点。val为新链表的节点HashMap<Node,Node> map = new HashMap<Node,Node>();Node cur = head;//遍历链表,设置map的key和valuewhile(cur != null){map.put(cur,new Node(cur.val));cur = cur.next;}cur = head;//再次遍历老链表,给新链表设置每一个节点的next和randomwhile(cur != null){//cur 老链表节点//map.get(cur) cur对应的新链表map.get(cur).next = map.get(cur.next);//设置新链表的nextmap.get(cur).random = map.get(cur.random);//设置新链表的randomcur = cur.next;}return map.get(head);}
}

2.5环形链表的判断

方法一:利用HashSet集合。

思路:遍历当前链表,每次遍历判断当前节点是否已经存在于set集合中。如果不存在,则把当前节点放入集合。如果已经存在,说明当前节点就是第一个入环节点。

/*** Definition for singly-linked list.* class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public boolean hasCycle(ListNode head) {//创建一个set,用于存放链表中已经遍历了的节点HashSet<ListNode> set = new HashSet<>();while(head != null){//如果当前节点已经存在于set,说明存在环形结构if(set.contains(head)) return true;set.add(head);head = head.next;}return false;}
}

方法二:快慢指针

开始时,快慢指针都在头节点的位置。快指针一次走两步,慢指针一次走一步。

如果没有环结构,快指针一定先走到尾节点。

如果有环结构,快慢指针会在换里相遇。而相遇所要走的卷数不会大于两圈。

相遇以后,快指针/慢指针到头节点的位置。两个指针开始一次走一步。最终两个指针会在第一次入换节点相遇!(原理就不证明了)

/*** Definition for singly-linked list.* class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public ListNode detectCycle(ListNode head) {if(head == null || head.next == null || head.next.next == null) return null;//定义慢指针,一次走一步ListNode n1 = head.next;//定义快指针,一次走两步ListNode n2 = head.next.next;while(n1 != n2){//当n1 n2不相遇时循环,所以我开始时没有把两个指针都设置在头节点的位置if(n2.next == null || n2.next.next == null){//说明没有环结构,直接返回空return null;}n1 = n1.next;//慢指针一次走一步n2 = n2.next.next;//慢指针一次走两步}//快指针移到头节点,开始一次走一步n2 = head;while(n1 != n2){//当两个指针相遇时,就走到了第一个入环节点n1 = n1.next;n2 = n2.next;}return n1;}
}

2.6 链表相交

思路:两个单链表,如何判断有没有相交点呢?

1.先遍历两个链表,到尾节点时停止。如果这时候,两个链表的尾节点都不想等。说明二者不相交。

2.如果二者尾节点是同一个,则计算二者链表长度的差值。让长的链表先走差值个距离。然后,短的链表从头开始走,二者一定会在相交点相遇!

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {//定义两个指针用于遍历两条链表ListNode cur1 = headA;ListNode cur2 = headB;int n = 0;//用于记录两条链表的差值while(cur1.next != null){cur1 = cur1.next;n++;}while(cur2.next != null){cur2 = cur2.next;n--;}if(cur1 != cur2){//尾节点都不想等,说明二者不相交return null;}//这样遍历完两条链表,n就是两条链表的长度差cur1 = n > 0 ? headA : headB;//让cur1指向两条链表中长的那一条cur2 = cur1 == headA ? headB : headA;//让cur2指向两条链表中短的那一条n = Math.abs(n);//n取绝对值while(n != 0){//让长的那条链表先移动两条链表差值的距离,再一起走,就会在相交部分汇合!cur1 = cur1.next;n--;}while(cur1 != cur2){cur1 = cur1.next;cur2 = cur2.next;}return cur1;}
}

2.7.两数相加

思路:

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode res = new ListNode();ListNode cur = res;int carry = 0;//当l1、l2中有一个不是空节点,或者还有进位,就继续循环while (l1 != null || l2 != null || carry != 0) {if (l1 != null) carry += l1.val;if (l2 != null) carry += l2.val;cur.next = new ListNode(carry % 10);//carry%10 就是该点的valcur = cur.next;carry = carry / 10;// carry/10 就是下一个点的进位if (l1 != null) l1 = l1.next;//l1 没有遍历完if (l2 != null) l2 = l2.next;}return res.next;}
}

2.8.合并两个有序链表

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode res = new ListNode();ListNode cur = res;while (list1 != null && list2 != null) {if (list1.val <= list2.val) {//如果l1链表的值更小cur = cur.next = list1;list1 = list1.next;} else {cur = cur.next = list2;list2 = list2.next;}}while (list1 != null) {//如果1还没遍历完cur = cur.next = list1;list1 = list1.next;}while (list2 != null) {//如果2还没遍历完cur = cur.next = list2;list2 = list2.next;}return res.next;}
}

2.9 反转链表2

题解参考:leetcode

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {ListNode dummy = new ListNode(0, head);ListNode g = dummy;ListNode p = dummy;//g指向的下一个节点就是要开始反转的节点for (int i = 0; i < left - 1; i++) {g = g.next;p = p.next;}//p指向第left个节点p = p.next;for (int i = 0; i < right - left; i++) {ListNode temp = p.next;p.next = p.next.next;temp.next = g.next;g.next = temp;}return dummy.next;}
}

2.10.K个一组反转链表

        本题思路和上一题差不多。举一反三,还是主要用g、p两个指针反转链表。

        每组链表反转之前,g的next指向的都是待反转链表的第一个节点,p指向的就是待反转链表的第一个节点。
        要注意的就是每次反转完链表p指针指向的就是反转后链表的最后一个元素,同时它的next也是下一组待反转链表的第一个元素,所以每次每组反转完以后,都要把p赋值给g。

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode dummy = new ListNode(0, head);ListNode g = head;//计算一共有多少个节点,用来算要反转几组链表int count = 0;while (g != null) {g = g.next;count++;}g = dummy;ListNode p = g.next;//遍历for (int i = 0; i < count / k; i++) {p = g.next;//反转的每组链表for (int j = 1; j < k; j++) {ListNode temp = p.next;p.next = p.next.next;temp.next = g.next;g.next = temp;}//每组链表反转完,让cur的next指向下一组待反转链表第一个g = p;}return dummy.next;}
}

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

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

相关文章

Invalid Executable The executable contains bitcode

Invalid Executable The executable contains bitcode 升级xcode16后&#xff0c;打包上传testflight时三方库报错&#xff1a;Invalid Executable - The executable ***.app/Frameworks/xxx.framework/xxx contains bitcode. 解决方案&#xff1a; 执行一下指令删除该framew…

软件测试学习路线图

软件测试工程师是专门从事软件、系统或产品测试和评估的技术专业人士&#xff0c;确保它们符合既定标准并无任何缺陷。通过精心设计和执行测试计划&#xff0c;软件测试工程师发现 Bug、故障和需要改进的领域&#xff0c;从而提高最终产品的可靠性和性能。 软件测试工程师在软…

Awcing 799. 最长连续不重复子序列

Awcing 799. 最长连续不重复子序列 解题思路: 让我们找到一个数组中&#xff0c;最长的 不包含重复的数 的连续区间的长度。 最优解是双指针算法&#xff1a; 我们用 c n t [ i ] cnt[i] cnt[i]记录 i i i 这个整数在区间内出现的次数。(因为每个数的大小为 1 0 5 10^5 105, …

状态模式原理剖析

《状态模式原理剖析》 状态模式&#xff08;State Pattern&#xff09; 是一种行为设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为。换句话说&#xff0c;当对象状态发生变化时&#xff0c;它的行为也会随之变化。 通过状态模式&#xff0c;可以消除通过 if-else…

从“可用”到“好用”,百度智能云如何做大模型的“超级工厂”?

如果说&#xff0c;过去两三年大模型处于造锤子阶段&#xff0c;那么今年&#xff0c;更多的则是考验钉钉子的能力&#xff0c;面对各类业务场景大模型是否能够有的放矢、一击必中&#xff0c;为千行百业深度赋能。 当前市场上&#xff0c;已经有200多把这样的锤子在疯狂找钉子…

【unity进阶知识1】最详细的单例模式的设计和应用,继承和不继承MonoBehaviour的单例模式,及泛型单例基类的编写

文章目录 前言一、不使用单例二、普通单例模式1、单例模式介绍实现步骤&#xff1a;单例模式分为饿汉式和懒汉式两种。 2、不继承MonoBehaviour的单例模式2.1、基本实现2.2、防止外部实例化对象2.3、最终代码 3、继承MonoBehaviour的单例模式3.1、基本实现3.2、自动创建和挂载单…

OCR 行驶证识别 离线识别

目录 正页识别 副页识别 全部识别 OCR 行驶证识别 离线识别 正页识别 副页识别 全部识别

电脑学习通看不到课程解决办法

电脑学习通看不到课程解决办法 查看学习通时发现没有课程 解决方法1: 更改单位 具体见:超星学习通关于PC版无法查看课程问题解决 解决方法二:添加应用 添加应用 点击账号管理 点击应用管理 添加应用、添加首页这个应用 添加完成后查看首页就能看到课程了 然后就OK啦、就可…

pcs集群表决盘故障导致主机reboot

建议重建fence设备并配置 PCSOracle HA实战安装配置参考 - 墨天轮

windows10使用bat脚本安装前后端环境之redis注册服务

首先需要搞清楚redis在本地是怎么安装配置、然后在根据如下步骤编写bat脚本&#xff1a; 思路 1.下载zip格式redis 2.查看windows server服务是否已安装redis 3.启动查看服务是否正常 bat脚本 echo off echo windows10 x64 server redis init REM 请求管理员权限并隐藏窗口 …

【牛Y】3DMAX快速构建低多边形城市建筑和道路插件CityBlocks教程

3DMAX快速构建低多边形城市建筑和道路插件CityBlocks&#xff0c;该插件功能主要分为两部分&#xff1a;一键城市建筑生成和一键城市道路生成。可用于城市配景建模、地图三维建模等使用。内置多种建筑组合方式&#xff0c;可使生成的建筑配景更加丰富、富于变换&#xff01; 【…

经纬恒润全冗余R-EPS助力L4级自动驾驶落地

随着L4级别自动驾驶技术的逐步成熟与商业化进程加速&#xff0c;行业对车辆安全性的要求达到了新的高度。为了确保自动驾驶车辆全天候、全路况下安全运行&#xff0c;冗余系统的研发与应用成为关键。在这一背景下&#xff0c;经纬恒润开发了齿条式全冗余电动助力转向系统R-EPS&…

Python模拟真人鼠标轨迹算法

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型&#xff0c;如直线或曲线路径。然而&#xff0c;这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现&#xff0c;能够通过深度学习技术&#xff0c;学习并模拟更自然的鼠标移动行为。 二.鼠标轨迹算法实…

8610 顺序查找

### 思路 1. **创建顺序表**&#xff1a;从输入中读取元素个数和元素值&#xff0c;构造顺序表。 2. **顺序查找**&#xff1a;在顺序表中依次查找关键字&#xff0c;找到则返回位置&#xff0c;否则返回0。 ### 伪代码 1. **创建顺序表**&#xff1a; - 动态分配存储空间。…

Stable Diffusion零基础学习

Stable Diffusion学习笔记TOP10 sd学习笔记TOP10的修改版本&#xff1a;IP2P的模型文件跟配置文件未添加&#xff0c;Tile分块重采样和局部重绘的模型文件跟配置文件撰写错误已被修改 _插件篇之ControlNet功能篇 ControlNet目前支持的10多种预处理器&#xff0c;根据数据检测…

构建Python机器学习模型的8个步骤

本文旨在系统地介绍构建机器学习模型的基本步骤&#xff0c;并通过一个具体的实战案例——股票价格预测&#xff0c;展示这些步骤的实际应用。通过遵循这些步骤&#xff0c;读者可以更好地理解和掌握机器学习模型构建的全过程。 步骤一&#xff1a;定义问题 首先&#xff0c;我…

NLP 序列标注任务核心梳理

句向量标注 用 bert 生成句向量用 lstm 或 bert 承接 bert 的输出&#xff0c;保证模型可以学习到内容的连续性。此时 lstm 输入形状为&#xff1a; pooled_output.unsqueeze(0) (1, num_sentence, vector_size) 应用场景 词性标注句法分析 文本加标点 相当于粗粒度的分词任…

RK3568笔记六十三:基于LVGL的Linux相机

若该文为原创文章,转载请注明原文出处。 记录移植韦老师的基于LVGL的Linux相机项目,主要是想学习如何在LVGL下显示摄像头数据。 此项目是基于老师的源码框架移植的,地址是lv_100ask_linux_camera: 基于LVGL的Linux相机 (gitee.com) 个人使用的是RK3568,正点原子板子,所以…

数据链路层 ——MAC

目录 MAC帧协议 mac地址 以太网帧格式 ARP协议 ARP报文格式​编辑 RARP 其他的网络服务或者协议 DNS ICMP协议 ping traceroute NAT技术 代理服务器 网络层负责规划转发路线&#xff0c;而链路层负责在网络节点之间的转发&#xff0c;也就是"一跳"的具体传输…

NLP 主流应用方向

主流应用 文本分类文本匹配序列标注生成式任务 应用细分 常见落地应用举例&#xff1a; 文本纠错句法分析文本翻译话者分离 本质为文本分类任务数字归一化 实现数字映射&#xff0c;提高内容可读性 如将一九九九转1999