链表
61. 旋转链表
给你一个链表的头节点 head
,旋转链表,将链表每个节点向右移动 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 {
public ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null || k == 0) return head;
ListNode dummy = new ListNode(0, head);
ListNode pre = dummy;
int len = 0;
while (pre.next != null) {
pre = pre.next;
len++;
}
ListNode tail = pre;
pre = dummy;
for (int i = 0; i < len - k % len; i++) {
ListNode cur = pre.next;
pre.next = cur.next;
cur.next = tail.next;
tail.next = cur;
tail = cur;
}
return dummy.next;
}
}
82. 删除排序链表中的重复元素 II
给定一个已排序的链表的头 head
, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode dummy = new ListNode(0);
ListNode pre = dummy;
ListNode cur = head, tail = head;
while (cur != null) {
while (tail != null && tail.val == cur.val) {
tail = tail.next;
}
// 如果没有相同的 1 2 此时cur指向1,tail指向2
// 如果有相同的 1 1 2,此时cur指向1,tail指向2
// 通过 cur.next == tail 判断是否cur是否重复
if (cur.next == tail) {
pre.next = cur;
pre = cur;
} else {
pre.next = tail;
}
cur = tail;
}
return dummy.next;
}
}
141. 环形链表
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true
。 否则,返回 false
。
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head, fast = head;
// 由于fast在slow前面所以只要判断fast是否为空就可以,不然就是 slow != null && fast != null && fast.next != null
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return true;
}
}
return false;
}
}
142. 环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
概括一下:
根据:
f = 2s (快指针每次2步,路程刚好2倍)
f = s + nb (相遇时,刚好多走了n圈)
推出:s = nb
从head结点走到入环点需要走 : a + nb, 而slow已经走了nb,那么slow再走a步就是入环点了。
如何知道slow刚好走了a步? 从head开始,和slow指针一起走,相遇时刚好就是a步
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) return null;
ListNode fast = head, slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
while (head != slow) {
head = head.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
⭐148. 排序链表
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
进阶:
- 你可以在
O(nlogn)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
class Solution {
// // 自底向上归并排序,O(1)空间复杂度,O(nlgn)时间复杂度
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
int len = 0;
ListNode p = head;
while (p != null) {
len++;
p = p.next;
}
ListNode dummy = new ListNode(0, head);
for (int sub_len = 1; sub_len < len; sub_len <<= 1) {
ListNode prev = dummy, cur = head;
// sub_len sub_len的起点为cur
// prev sub_len sub_len 重新赋值prev
while (cur != null) {
ListNode first = cur;
for (int i = 0; i < sub_len - 1 && cur != null && cur.next != null; i++) {
cur = cur.next;
}
ListNode second = cur.next;
cur.next = null;
cur = second;
for (int i = 0; i < sub_len - 1 && cur != null && cur.next != null; i++) {
cur = cur.next;
}
ListNode third = null;
// 这个时候cur可能已经没了
if (cur != null) {
third = cur.next;
cur.next = null;
}
ListNode merged = merge(first, second);
prev.next = merged;
while (prev.next != null) {
prev = prev.next;
}
cur = third;
}
}
return dummy.next;
}
// 自顶向下归并排序,O(lgn)空间复杂度,O(nlgn)时间复杂度
public ListNode sortList (ListNode head) {
if (head == null || head.next == null) return head;
return mergeSort(head);
}
private ListNode mergeSort (ListNode head) {
if (head == null || head.next == null) return head;
ListNode prev = new ListNode(0, head);
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
prev = prev.next;
slow = slow.next;
fast = fast.next.next;
}
prev.next = null;
ListNode left = mergeSort(head);
ListNode right = mergeSort(slow);
return merge(left, right);
}
private ListNode merge(ListNode a, ListNode b) {
ListNode dummy = new ListNode();
ListNode p = dummy;
while (a != null && b != null) {
if (a.val < b.val) {
p.next = a;
a = a.next;
} else {
p.next = b;
b = b.next;
}
p = p.next;
}
if (a != null) {
p.next = a;
}
if (b != null) {
p.next = b;
}
return dummy.next;
}
}
⭐160. 相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
// 通过计算长度差,让较长的先走这个差值,然后一起走,第一次指针指向相同则返回
// 时间复杂度:O(m+n)
// 空间复杂度:O(1)
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode dummyA = new ListNode(0, headA), dummyB = new ListNode(0, headB);
ListNode pA = dummyA, pB = dummyB;
while (pA.next != null && pB.next != null) {
pA = pA.next;
pB = pB.next;
}
if (pA.next == null) {
ListNode temp = pA;
pA = pB;
pB = temp;
ListNode temp_dummy = dummyA;
dummyA = dummyB;
dummyB = temp_dummy;
}
int cnt = 0;
while (pA.next != null) {
pA = pA.next;
cnt++;
}
for (int i = 0; i < cnt; i++) {
dummyA = dummyA.next;
}
while (dummyA != dummyB) {
dummyA = dummyA.next;
dummyB = dummyB.next;
}
return dummyA;
}
// 遍历一个链表将节点存入hashset,然后遍历另一个链表,遍历查看是否在集合中,在的话返回
// 时间复杂度:O(m+n)
// 空间复杂度:O(m)
public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();
ListNode p = headA;
while (p != null) {
set.add(p);
p = p.next;
}
p = headB;
while (p != null) {
if (set.contains(p)) {
return p;
}
p = p.next;
}
return null;
}
// A: --------******
// B: ---******
// A+B: --------abcdefg---abcdefg
// B+A: ---abcdefg--------abcdefg
// 遍历完了再遍历另一条链表,第一个相同的则返回
// 时间复杂度:O(m+n)
// 空间复杂度:O(1)
public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA != null ? pA.next : headB;
pB = pB != null ? pB.next : headA;
}
return pA;
}
}
反转链表
⭐206. 反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
class Solution {
// 迭代:设置dummy每次新节点进行头插
public ListNode reverseList(ListNode head) {
ListNode dummy = new ListNode();
ListNode cur = head, temp = null;
while (cur != null) {
temp = cur.next;
cur.next = dummy.next;
dummy.next = cur;
cur = temp;
}
return dummy.next;
}
// 递归:
public ListNode reverseList1(ListNode head) {
if (head == null || head.next == null) return head;
ListNode new_head = reverseList1(head.next);
head.next.next = head;
head.next = null;
return new_head;
}
}
⭐92. 反转链表 II
难度中等1180
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
/**
* 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 reverseBetween(ListNode head, int left, int right) {
// 可以把2、3分别头插到4后面,需要定位到1和4,然后在对2、3进行处理
// 也可以把3、4分别头插到1后面,需要定位到1,然后对2、3进行处理,更快
ListNode dummy = new ListNode(0, head);
ListNode pre = dummy;
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
ListNode temp = pre.next;
for (int i = 0; i < right - left; i++) {
ListNode cur = temp.next;
temp.next = cur.next;
cur.next = pre.next;
pre.next = cur;
}
return dummy.next;
}
}
24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
/**
* 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 pre = dummy, tail = pre;
while (true) {
for (int i = 0; i < 2; i++) {
if (tail != null) {
tail = tail.next;
}
}
if (tail == null) break;
ListNode cur = pre.next;
pre.next = cur.next;
cur.next = tail.next;
tail.next = cur;
pre = cur;
tail = cur;
}
return dummy.next;
}
}
⭐⭐⭐25. K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
// 每个子序列,使每个最前面的结点尾插到该序列当前tail后面,tail维持不动
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0, head);
ListNode pre = dummy, tail = dummy;
while (true) {
for (int i = 0; i < k; i++) {
if (tail != null) {
tail = tail.next;
}
}
if (tail == null) break;
ListNode curHead = pre.next;
while (pre.next != tail) {
ListNode cur = pre.next; // 取出操作的结点
pre.next = cur.next; // 断链
cur.next = tail.next; // 尾插到tail后面
tail.next = cur;
}
pre = curHead;
tail = curHead;
}
return dummy.next;
}
}