链表定义
/**
* 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; }
* }
*/
反转链表
迭代
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
迭代
迭代思路是遍历到最后一个节点的返回栈中进行节点的修改。
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
栈
使用数据结构栈的思路和迭代思路相似,但更容易理解。就是往栈里不断的放节点,直到节点放完,然后从栈里取出来构建一个新的链表。思路和递归一样,只是不用嵌套。
寻找链表的中点
快慢指针,快指针比慢指针始终走快一步。
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
合并链表
public void mergeList(ListNode l1, ListNode l2) {
ListNode l1_tmp;
ListNode l2_tmp;
while (l1 != null && l2 != null) {
l1_tmp = l1.next;
l2_tmp = l2.next;
l1.next = l2;
l1 = l1_tmp;
l2.next = l1;
l2 = l2_tmp;
}
}
重排链表
描述:L0 → L1 → … → Ln - 1 → Ln =》 L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
思路便是以上的合集:先找到链表的中点,然后反转链表中点后的链表。将两个链表合并。
class Solution {
public void reorderList(ListNode head) {
if (head == null) {
return;
}
ListNode mid = middleNode(head);
ListNode l1 = head;
ListNode l2 = mid.next;
mid.next = null;
l2 = reverseList(l2);
mergeList(l1, l2);
}
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
public void mergeList(ListNode l1, ListNode l2) {
ListNode l1_tmp;
ListNode l2_tmp;
while (l1 != null && l2 != null) {
l1_tmp = l1.next;
l2_tmp = l2.next;
l1.next = l2;
l1 = l1_tmp;
l2.next = l1;
l2 = l2_tmp;
}
}
}
环形链表
返回链表开始入环的第一个节点。
思路:使用两个指针,fast与slow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与slow 指针在环中相遇。
设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与fast 相遇。此时,fast 指针已经走完了环的 n 圈,因此它走过的总距离为
a+n(b+c)+b=a+(n+1)b+nca+n(b+c)+b=a+(n+1)b+nc。
而任意时刻,fast 指针走过的距离都为slow 指针的 22 倍。因此,我们有
a+(n+1)b+nc=2(a+b) => a=c+(n-1)(b+c)
a+(n+1)b+nc=2(a+b) => a=c+(n−1)(b+c)
所以推出:从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。
因此在第一次相遇的时候,我们引入另外一个指针,从头开始走,当指针和慢指针相遇的时候,就是环的入口节点。
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (fast == slow) {
ListNode ptr = head;
while (ptr != slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}
}
链表排序
todo