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

Leetcode 刷题记录 08 —— 链表第二弹

本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C++语言,08及以后为Java语言。

01 合并两个有序链表

在这里插入图片描述

/*** 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) {}
}

方法一:递归

class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {if(list1 == null){return list2;}else if(list2 == null){return list1;}else if(list1.val < list2.val){list1.next = mergeTwoLists(list1.next, list2);return list1;}else{list2.next = mergeTwoLists(list1, list2.next);return list2;}}
}

方法二:遍历

class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode list3 = new ListNode(-1);ListNode flag = list3; //论标记的重要性while(list1 != null && list2 != null){if(list1.val < list2.val){list3.next = list1;list1 = list1.next;}else{list3.next = list2;list2 = list2.next;}list3 = list3.next;}if(list1 != null){list3.next = list1;}else{list3.next = list2;}return flag.next; //返回头结点指针}
}

02 两数相加

在这里插入图片描述

在这里插入图片描述

方法一:设新链表、进位值,不断创建新节点

/*** 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 start = null, end = null;int flag = 0;while(l1 != null || l2 != null){//1. 计算单位加和int n1 = l1 != null ? l1.val : 0;int n2 = l2 != null ? l2.val : 0;int sum = n1 + n2 + flag;//2. 添加新链表结点,计算进位if(start == null){start = end = new ListNode(sum % 10);}else{end.next = new ListNode(sum % 10);end = end.next;}flag = sum / 10;//3. 移动结点if(l1 != null){l1 = l1.next;} if(l2 != null){l2 = l2.next;} }//4. 特殊情况:高位进一if(flag > 0){end.next = new ListNode(flag);}return start;}
}

03 删除链表的倒数第N个结点

在这里插入图片描述

在这里插入图片描述

/*** 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 removeNthFromEnd(ListNode head, int n) {ListNode dummy = new ListNode(0, head);ListNode first = head;ListNode second = dummy; //可移动结点//1. first移动n位for(int i=0; i<n; ++i){first = first.next;}//2. first、second同时移动,first=null为止while(first != null){first = first.next;second = second.next;}//3. 删除结点,释放空间second.next = second.next.next;return dummy.next;}
}

dummy.next不是head吗,那么ListNode ans = dummy.next; return ans;存在的意义是什么?

如果 n 等于链表的长度,即需要删除头节点时,单独使用 head 会导致很复杂的边界条件处理,在这种情况下,dummy 提供一个可靠的起始节点,方便统一处理删除节点的逻辑。

② 为什么要新建一个链表ListNode dummy = new ListNode(0, head);,而不是在原有链表上操作?

dummy节点简化了边界条件的处理,所有节点,无论是否是头节点,都可以以一致的方式处理,最终可以直接返回dummy.next作为结果,这样不会影响原始链表的头节点指向。

04 两两交换链表中的节点

在这里插入图片描述

在这里插入图片描述

/*** 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 swapPairs(ListNode head) {ListNode dummy = new ListNode(0, head);ListNode flag = dummy; //可移动结点while(flag.next != null && flag.next.next != null){//1. 顺次两个节点ListNode a = flag.next;ListNode b = flag.next.next;//2. 交换flag.next = b;a.next = b.next;b.next = a;flag = a;}return dummy.next; //万金油}
}

05 K个一组翻转链表

在这里插入图片描述

在这里插入图片描述

/*** 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 {//需要pre,head,tail,nex四个指针public ListNode reverseKGroup(ListNode head, int k) {//1.创建dummy链表,返回dummy.nextListNode dummy = new ListNode(0, head);ListNode pre = dummy;//2.寻找(head,tail),调用myReverse方法while(head != null){ListNode tail = pre;for(int i=0; i<k; ++i){tail = tail.next;if(tail == null){return dummy.next;}}ListNode nex = tail.next;ListNode[] reverse = myReverse(head, tail);head = reverse[0];tail = reverse[1];//3.连接断裂链表pre.next = head;tail.next = nex;pre = tail;head = nex;}return dummy.next;}//需要a,b,c三个指针public ListNode[] myReverse(ListNode start, ListNode end){//1.寻找a,b,c//2.核心操作:a->c//3.临界条件:end==cListNode a = start;ListNode c = end.next;while(end != c){ListNode b = a.next;a.next = c;c = a;a = b;}return new ListNode[]{end, start};}
}

06 随机链表的复制

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

失败代码

/*
// 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) {//1.创建dummy链表,返回dummy.nextNode dummy = new Node(0);dummy.next = head;//2.第一次遍历,复制结点值和next指针Node end = dummy;Node start = head;while(start != null){end.next = new Node(start.val); //复制结点值end = end.next;end.next = start.next; //复制next指针start = start.next;}//3.第二次遍历,复制random指针Node end2 = dummy;Node start2 = head;while(start2 != null){end2 = end2.next;end2.random = start2.random; //复制random指针start2 = start2.next;}return dummy.next;}
}

问题:当前复制的 random 指针仍然指向原链表中的节点,而不是新复制链表中的节点。

方法一:递归

/*
// 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 {//1.创建映射Map<Node, Node> cachedNode = new HashMap<Node, Node>();public Node copyRandomList(Node head) {if(head == null){return null;}//2.创建新节点,拷贝valif(!cachedNode.containsKey(head)){Node headNew = new Node(head.val);cachedNode.put(head, headNew);//3.递归,拷贝next,randomheadNew.next = copyRandomList(head.next);headNew.random = copyRandomList(head.random);}return cachedNode.get(head);}
}

方法二:结点拆分

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

/*
// 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) {if(head == null){return null;}//1. 一次遍历:节点插入,拷贝valfor(Node node = head; node != null; node = node.next.next){Node nodeNew = new Node(node.val);nodeNew.next = node.next;node.next = nodeNew;}//2. 二次遍历:拷贝randomfor(Node node = head; node != null; node = node.next.next){Node nodeNew = node.next;nodeNew.random = (node.random != null) ? node.random.next : null;}//3. 三次遍历:拷贝nextNode headNew = head.next;for(Node node = head; node != null; node = node.next){Node nodeNew = node.next;node.next = node.next.next;nodeNew.next = (node.next != null) ? nodeNew.next.next : null;}return headNew;}
}

07 排序链表

在这里插入图片描述

在这里插入图片描述

方法一:快慢指针归并排序 + 合并有序链表

/*** 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 sortList(ListNode head) {return sortList(head, null); //没说区间,默认右端点为null }//1.伪二分(快慢指针)public ListNode sortList(ListNode head, ListNode tail){// if(head == null || head.next == tail){ //特殊情况判断//     return head;// }if (head == null) {return head;}if (head.next == tail) {head.next = null;return head;}ListNode fast = head;ListNode slow = head;while(fast != tail){fast = fast.next;slow = slow.next;if(fast != tail){fast = fast.next;}}//2.归并排序(head,tail)ListNode mid = slow;ListNode list1 = sortList(head, mid);ListNode list2 = sortList(mid, tail);ListNode sorted = mergeList(list1, list2);return sorted;}//3.合并两个有序链表public ListNode mergeList(ListNode l1, ListNode l2){ListNode l3 = new ListNode(0);ListNode flag = l3; //return flag.next;while(l1 != null && l2 != null){if(l1.val < l2.val){l3.next = l1;l1 = l1.next;}else{l3.next = l2;l2 = l2.next;}l3 = l3.next;}if(l1 != null){l3.next = l1;}if(l2 != null){l3.next = l2;}return flag.next;}
}

方法二:倍增长度 + 合并两个有序链表

/*** 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 sortList(ListNode head) {if(head == null){ //特殊情况判断return head;}//1.获取链表长度lengthint length = 0;ListNode node = head;while(node != null){length++;node = node.next;}//2.倍增长度subLength//a.四指针 pre/head1/head2/nex//b.两断链 //c.一接骨ListNode dummy = new ListNode(0, head);for(int subLength = 1; subLength < length; subLength <<= 1){ListNode pre = dummy;ListNode curr = dummy.next;while(curr != null){ListNode head1 = curr;for(int i=1; i<length && curr.next != null; i++){curr = curr.next;}ListNode head2 = curr;curr.next = null;curr = head2;for(int i=1; i<length && curr.next != null && curr != null; i++){curr = curr.next;}ListNode nex = null;if(curr.next != null){nex = curr.next;curr.next = null;}ListNode merged = mergeList(head1, head2);pre.next = merged;pre = curr;curr = nex;}}//3.合并方法return dummy.next;}//合并两个有序链表public ListNode mergeList(ListNode l1, ListNode l2){ListNode l3 = new ListNode(0);ListNode flag = l3; //return flag.next;while(l1 != null && l2 != null){if(l1.val < l2.val){l3.next = l1;l1 = l1.next;}else{l3.next = l2;l2 = l2.next;}l3 = l3.next;}if(l1 != null){l3.next = l1;}if(l2 != null){l3.next = l2;}return flag.next;}
}

.next;
}

//合并两个有序链表
public ListNode mergeList(ListNode l1, ListNode l2){ListNode l3 = new ListNode(0);ListNode flag = l3; //return flag.next;while(l1 != null && l2 != null){if(l1.val < l2.val){l3.next = l1;l1 = l1.next;}else{l3.next = l2;l2 = l2.next;}l3 = l3.next;}if(l1 != null){l3.next = l1;}if(l2 != null){l3.next = l2;}return flag.next;
}

}


相关文章:

  • LeetCode 热题 100 279. 完全平方数
  • 【Spring Boot 注解】@SpringBootApplication
  • homebrew安装配置Python(MAC版)
  • “wsl --install -d Ubuntu-22.04”下载慢,中国地区离线安装 Ubuntu 22.04 WSL方法(亲测2025年5月6日)
  • 网络安全等级保护有关工作事项[2025]
  • 网工实验——静态路由与BFD联动
  • 如何将本地 Jar 包安装到 Maven 仓库(以 Aspose 为例)
  • vue3 computed方法使用详细讲解
  • MUSIQ ,MANIQA,CLIP-IQA,FID是什么指标,分别是如何计算的(图像恢复领域评价指标
  • sqli-labs靶场通关保姆级教学(Get传输篇)Less-1Less-10
  • Python 10天冲刺 《元编程(Meta-programming)》
  • 《Python星球日记》第33天:前后端分离与 RESTful API
  • RPM打包格式spec文件设计原理与关键特性说明
  • 2. H264编码
  • 跨境电商合规新时代:亚马逊AHR风控系统深度解析与应对指南
  • 视觉爬虫开发:通过Puppeteer截图+CV定位动态元素坐标
  • 【iOS】源码阅读(二)——NSObject的alloc源码
  • ECMAScript 6(ES6):JavaScript 现代化的革命性升级
  • 1、Kafka与消息队列核心原理详解
  • 【技术追踪】通过潜在扩散和先验知识增强时空疾病进展模型(MICCAI-2024)
  • 观察|22项达全球最优,世行为上海营商环境“盖章”
  • 川大全职引进考古学家宫本一夫,他曾任日本九州大学副校长
  • 巴方称印军发动24起袭击,巴境内6处地点遭袭致8人死亡
  • 视频丨054B型护卫舰钦州舰南海实战化训练
  • 张求会谈陈寅恪的生前身后事
  • 申活观察|精致精准精细,城市“双面镜”照见怎样的海派活力