文章目录
(LeetCode 21)合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
方法一 循环+双指针
定义一个结果链表,比较两链表结点的大小,小的放在结果链表上。
此处将resultNode赋给p是为了链接结点,resultNode始终是结果链表的首位 ,但返回的是resultNode.next
时间复杂度 O(n+m) 空间复杂度 O(1)
- 问题已理解
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
//先判断两链表是否有空的,有就直接返回另一个
if(list1 == null) return list2;
if(list2 == null) return list1;
//定义一个结果链表
ListNode resultNode = new ListNode(0);
ListNode p = resultNode;
//比较l1和l2各个节点的大小,将小的连到result链表上
while(list1 != null && list2 != null){
if(list1.val < list2.val){
p.next = list1;
list1 = list1.next;
}else{
p.next = list2;
list2 = list2.next;
}
p = p.next;
}
//l1、l2有任何一个连完了就会退出循环,此时把剩下的连到结果链表上
if(list1 != null){
p.next = list1;
}
if(list2 != null){
p.next = list2;
}
return resultNode.next;
}
方法二 递归
将问题拆分成一个略长的链表和一个去掉一个已连接到结果链表上的结点的略短的链表的合并问题,不断递归调用方法。行数少,但内存占用可能会大一点。涉及到出入栈
时间复杂度 O(n+m) 空间复杂度 O(n+m)
- 问题已理解
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null) return list2;
if(list2 == null) return list1;
if(list1.val < list2.val){
list1.next = mergeTwoLists(list1.next,list2);
return list1;
}
list2.next = mergeTwoLists(list1,list2.next);
return list2;
}
(LeetCode 83)删掉排序链表中重复的元素
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
方法一 遍历链表
遍历链表,当前结点的值与下一个相等就将下一个节点指向下下个结点
public ListNode deleteDuplicates(ListNode head) {
if(head == null){
return head;
}
ListNode currentNode = head;
while(null != currentNode.next){
if(currentNode.next.val == currentNode.val){
currentNode.next = currentNode.next.next;
}else{
currentNode = currentNode.next;
}
}
return head;
}
方法二 递归
缩小到最后一个结点和前一个结点比较大小,相同返回前一个结点,(也就是删除了重复的最后一个结点)不同返回最后那个结点
就拿倒数第二个结点来说,
head.next = deleteDuplicates(head.next); 这一步会调用方法deleteDuplicates,返回的是head.next,即 head.next =head.next,
然后return head.val == head.next.val?head.next : head;,比较这俩结点值的大小,返回其中之一,可以是任意一个。
- 问题已理解
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null) return head;
head.next = deleteDuplicates(head.next);
return head.val == head.next.val? head.next : head;
}
(LeetCode 82*)删掉排序链表中重复的元素Ⅱ
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
方法 遍历链表
- 问题已理解
由于本问题可能会去掉头结点,所以我们要单独定义一个结点,使其指向head
ListNode resultNode = new ListNode();
resultNode.next = head; 然后就遍历数组,遇到相同的结点数据记录下来,与后续结点数值比较,如果相同就将当前结点的下一个结点指向下下个结点;否则直接遍历下一个结点
最后返回的是resultNode.next,而不是head
public static ListNode deleteDuplicates(ListNode head) {
if(head == null){
return head;
}
ListNode resultNode = new ListNode();
resultNode.next = head;
ListNode currentNode = resultNode;
while (currentNode.next != null && currentNode.next.next != null) {
if (currentNode.next.val == currentNode.next.next.val) {
int val = currentNode.next.val;
while (currentNode.next != null && currentNode.next.val == val) {
currentNode.next = currentNode.next.next;
}
} else {
currentNode = currentNode.next;
}
}
return resultNode.next;
}
(LeetCode 141*)环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
方法 快慢指针
快慢两个指针遍历链表,如果“相遇或者说追上”说明有环(追击问题:速度分别是慢:1;快:2)
在两指针追上之前,慢指针绝对没有完整地遍历过一次环。
极限情况:慢指针开始进入环时,快指针恰好是在它下一个的位置。把慢1快2的速度看成相对的慢0快1,即 快结点最长也要经历 环长-1 才能追上慢结点,此外的各种情况,快结点的路程都小于环长-1,从而慢结点的路程也要小于环长了(感觉解释有点牵强,我在想想别的解法)。
同时环形链表只要有环就没有结点会指向null
- 问题未理解
public boolean hasCycle(ListNode head) {
if(head == null) return false;
ListNode slowPtr = head,fastPtr = head;
while(fastPtr.next != null && fastPtr.next.next != null){
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
if(slowPtr == fastPtr){
return true;
}
}
return false;
}
(LeetCode 142*)环形链表Ⅱ
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
为了表示给定链表中的环,评测系统内部使用整数 pos来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。
注意:pos不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改链表
方法 快慢指针
当找到环后,慢指针回到头结点,快指针再以和慢指针一样的速度开始遍历。
很神奇,当确定链表里有环后,慢指针从开头开始遍历,快指针从当前位置开始遍历,两者速度一样。再次相遇的点就是环的开始点
快指针是慢指针速度的两倍:2(a+b) = a + b +k * (b + c)
即 a - c = (k - 1) * (b + c)
这个说明了相遇后慢指针从头开始,快指针从相遇位置开始,相同的速度,他们经过相同的时间,之间的路程差始终是环长的整数倍,也就是下次相遇的位置是环开始的位置
- 问题未理解
public ListNode detectCycle(ListNode head) {
if(head == null) return null;
ListNode slowPtr = head,fastPtr = head;
boolean loopExists = false;
while(fastPtr.next != null && fastPtr.next.next != null){
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
if(slowPtr == fastPtr){
loopExists = true;
break;
}
}
if(loopExists){ //环存在
slowPtr = head;
while(slowPtr != fastPtr){
fastPtr = fastPtr.next;
slowPtr = slowPtr.next;
}
return slowPtr; //返回环开始的结点
}
return null; //不存在
}