上一篇文章:【力扣刷题】Day03——链表专题_塔塔开!!!的博客-CSDN博客
题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)
思路:由于是一对一对的进行交换,因此我们枚举的时候也要一对一对的来。
尤其要注意
Code
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0);
dummy.next = head;
// 一对一对的枚举
for(ListNode p = dummy; p.next != null && p.next.next != null;){
ListNode a = p.next;
ListNode b = a.next;
// 交换
p.next = b;// 1
a.next = b.next;// 2
b.next = a;// 3
// 指针移动,为下一次做好准备
p = a;// 4
}
return dummy.next;
}
}
题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
要删除节点就要找到要删除节点的前驱在哪:
思路一:枚举找到要删除节点的前驱
当头节点会改变时我们引入虚拟头节点
Code
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
int len = 0, cnt = 0;
for(ListNode cur = dummy; cur != null; cur = cur.next) len ++;
for(ListNode cur = dummy; cur != null; cur = cur.next){
cnt ++;
if(len - n == cnt){// 找到倒数第n+1得位置
cur.next = cur.next.next;
break;
}
}
return dummy.next;
}
}
思路二:双指针优化:双指针找到要删除节点的前驱
上述得关键是要找到删除节点得前一个节点,采用的是遍历的方式,现在我们用双指针进行优化:
1、创建虚拟结点dummy
,dummy指向head.next
2、指针first
,second
初始化均指向dummy
,first
指针走n
步,first,second
指针同时向后走,直到first
走到末尾时终止
3、这样找到了删除结点的前一个结点,让前一个结点指向删除结点的后一个结点即可
Code
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode first = dummy;
ListNode second = dummy;
for(int i = 0; i < n; i ++) first = first.next;// first先往后走n步
// 注意:这里是first.next != null 而不是first
// 因为当指针走到最后一个节点了,first它不为空,还会再执行一次循环体,这样就不对了
while(first.next != null){
first = first.next;
second = second.next;
}
second.next = second.next.next;
return dummy.next;
}
}
题目链接:160. 相交链表 - 力扣(LeetCode)
一种非常巧妙得思路,分别给链表A和链表B设置指针A和指针B,然后开始遍历链表,如果当前链表走完了,则将指针指向另一个链表得头部继续遍历,直到两个指针相遇。
两个指针走过得路径长度分别为:
a,b 分别代表两个链表的长度,则两个指针分别走 a+b 步后都变成 null,最终null相等
两个链表相交:
则两个指针分别走 a+b+c 步后在两链表交汇处相遇。
Code
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p = headA;
ListNode q = headB;
while(p != q){
if(p == null) p = headB;
else p = p.next;
if(q == null) q = headA;
else q = q.next;
}
return p;
}
}
题目链接:142. 环形链表 II - 力扣(LeetCode)
解法一:哈希判重,入口即为第一次重复的节点
空间复杂度:O(n)
Code
/**
* 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) {
Set<ListNode> set = new HashSet<ListNode>();
for(ListNode p = head; p != null; p = p.next){
if(set.contains(p)) return p;
set.add(p);
}
return null;
}
}
解法二:快慢指针
空间复杂度:O(1)
思路:使用快慢指针,快指针每次走两步,满指针每次走一步,当两个指针第一次相遇的时候,将其中一个指针(快)回到起点,然后快慢指针一起每次走一步,再次相遇即为环的入口。
假设C点为两个指针的相遇点
第一次相遇:
x + y
x + y + n(y + z)
,快指针可能转了好几圈不妨直接设快指针转了
1
圈,由2倍得出等式:
2(x + y) = x + y + (y + z)
化简得:
x = z
而此时又是在第一次相遇的点(C),将其中一个指针(快)回到起点,然后快慢指针一起每次走一步,再次相遇即为环的入口(距离都是一样的,都为
z
)。
详细解答证明:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili
Code
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
// 快慢指针用起点开始,快走两步,慢走一步
// 当快慢第一次相遇时,快回到起点,然后快慢一起走一步,再次相遇时即为环的入口
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(slow == fast){// 第一次相遇
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
// 第二次相遇即为环的入口
return fast;
}
}
return null;
}
}
题目链接:83. 删除排序链表中的重复元素 - 力扣(LeetCode)
Code
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode p = head;
while(p != null){
if(p.next != null && p.val == p.next.val) p.next = p.next.next;
else p = p.next;
}
return head;
}
}
题目链接:82. 删除排序链表中的重复元素 II - 力扣(LeetCode)
这题与上一题的区别是,一旦元素出现重复就要全部删除掉,而上一题要保留一个
之前我们是一个一个的删除,现在我们要对连续一段(相同值)进行删除,通过双指针实现:
由于可能要删除头节点,因此设置虚拟节点!
时间复杂度:O(n)
空间复杂度:O(1)
Code
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode p = dummy;
while(p.next != null){
ListNode q = p.next;
while(q.next != null && q.val == q.next.val) q = q.next;
if(q == p.next) p = p.next;// 说明没有重复节点 p移动
else p.next = q.next;// 删除重复区间的节点
}
return dummy.next;
}
}
题目链接:328. 奇偶链表 - 力扣(LeetCode)
思路:先分离后合并,双指针,交替移动即可。
时间复杂度:O(n)
空间复杂度:O(1)
Code
class Solution {
public ListNode oddEvenList(ListNode head) {
if(head == null) return head;
ListNode ji = head;
ListNode ou_head = head.next;
ListNode ou = ou_head;
// 奇偶交替拼接,最后两个链再拼一起
while(ou != null && ou.next != null){
ji.next = ou.next;
ji = ou.next;
ou.next = ji.next;
ou = ji.next;
}
// 将奇偶链拼接
ji.next = ou_head;
return head;
}
}