两两交换链表中的节点
题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/
思路
- 递归。时间O(n),空间O(n)
- 迭代。理解简单,但写起来复杂。时间O(n),空间O(1)
代码
//递归
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode one = head, two = one.next, three = two.next;
two.next = one;
one.next = swapPairs(three);
return two;
}
}
19. 删除链表的倒数第 N 个结点
题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
思路
- 一个指针,两次遍历。时间O(n),空间O(1)
- 两个指针,一次遍历。快指针和慢指针之间有n-1个节点。时间O(n),空间O(1)
代码
//两次遍历
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int len = 0;
ListNode p = head;
while (p != null) {
len++;
p = p.next;
}
p = head;
//删除第一个节点
if (n == len) {
return head.next;
}
//找到待删除节点的前一个结点
for (int i = 1; i < len - n; i++) {
p = p.next;
}
p.next = p.next.next;
return head;
}
}
//一次遍历
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast = head, slow = head;
//fast先走n步
while (n != 0) {
fast = fast.next;
n--;
}
//删除第一个节点
if (fast == null) {
return head.next;
}
//fast和slow同时走
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
//此时slow指向待删除的前一个节点
slow.next = slow.next.next;
return head;
}
}
面试题 02.07. 链表相交
题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
思路
- 定义p遍历A,q遍历B。如果链接相交,则一定有公共的节点,即p=q。相交的起始节点就是第一次p=q的时候。注意p和q要同时遍历且遍历时链表的长度要相等。时间O(m+n),空间O(1)
代码
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p = headA, q = headB;
int lenA = 0, lenB = 0;
//求出两个链表的长度
while (p != null) {
lenA++;
p = p.next;
}
while (q != null) {
lenB++;
q = q.next;
}
//回到头节点
p = headA;
q = headB;
//保证链表遍历的长度相等,lenA=lenB
if (lenA > lenB) {
while (lenA != lenB) {
p = p.next;
lenA--;
}
} else if (lenA < lenB) {
while (lenA != lenB) {
q = q.next;
lenB--;
}
}
//开始一起遍历
while (p != q) {
p = p.next;
q = q.next;
}
return p;
}
}
142. 环形链表 II
题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/description/
思路
- 用set集合存储节点。遍历一遍链表,如果节点重复出现,那么该节点就是入环的第一个节点。时间O(n),空间O(n)
- 快慢指针。快指针一次走两步,慢指针一次走一步。相遇时慢指针回到头结点重新走,此时快慢指针一次都走一步。具体数学推导见解析。时间O(n),空间O(1)
代码
//HashSet
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
ListNode p = head;
while (p != null) {
if (set.contains(p)) {
return p;
}
set.add(p);
p = p.next;
}
return null;
}
}
//快慢指针
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode fast = head.next.next, slow = head.next;
while (fast != slow && fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
if (fast != slow) {
return null;
}
slow = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}