旋转链表
- 思路 : 旋转 k 个位置 , 第 k 个位置 即为新头, 1 -> 2 -> 3 -> 4 旋转2 个 4 -> 1 -> 2 -> 3 3 -> 4 -> 1-> 2 使得链表尾部连接头部 这样就可以循环遍历 找到 链表的 第 k - 1 个位置 断开后面的位置 即可完成旋转链表 。
- 细节: 考虑到 k 可能大、移动次数过多、取k % n(链表长度) 效果是等效的。
- 快慢指针法: 两个指针 f 和 s 、 f 先走 k 步,s 在动 ,f 走到头 、 s 刚好指向分界点、可以参考删除链表的第N个节点 把链表从分界点断开 、将尾部 f 指向 head 即可。
- 双指针图解
code
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null || k == 0) return head;
// 记录 newHead 的 前一个节点 用于 置空 防止 成环
ListNode newHead = head;
// 链表尾部 连接原来链表
ListNode listTail = null;
int n = 0;
while (newHead != null) {
n ++;
// null 的前驱节点 即为 链表尾部
listTail = newHead;
newHead = newHead.next;
}
newHead = head;
// 要走的步数
int stp = k % n;
if (stp == 0) return head;
// 首尾连接 之后断开
listTail.next = head;
// 又换之后 遍历链表
for (int i = 0 ; i < n - stp ; i ++) {
listTail = listTail.next;
}
newHead = listTail.next;
listTail.next = null;
return newHead;
}
}
快慢指针法
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null || k == 0) return head;
// 调整之前 合法性判断
ListNode f = head;
int n = 0;
while (f != null) {
f = f.next;
n ++;
}
k = k % n;
if (k == 0) return head;
f = head;
ListNode s = head;
for (int i = 0; i < k; i ++,f = f.next);
for ( ; f.next != null; s = s.next,f = f.next);
f.next = head;
head = s.next;
s.next = null;
return head;
}
}
重排链表
- 题意 :L1 -> L2 -> L3 -> ··· Ln 转化为 L1 --> Ln --> L2 --> Ln - 1 ···
- 思路 : 很明显看到 正序 逆序 穿插放置的方式重排链表 所以正序 逆序 应该各占一半 画图模拟 奇数情况 和 偶数情况
code (代码详细注释 )
class Solution {
public void reorderList(ListNode head) {
ListNode s = head;
ListNode f = head;
// 偶数 取 中下节点
// f.next != null && f.next.next != null 保证偶数节点 一定会 走到 链表尾部的前一个节点上(这仅仅是我的观点)
// 奇数 取 中间节点
for(; f.next != null && f.next.next != null; s = s.next , f = f.next.next );
// 现在要做的是 翻转 [s , f] 区间的所有节点
ListNode pre = s;
s = s.next;
// 断开 防止 有环
pre.next = null;
// f 作为 反转节点的头节点
f = reserve(head,s,f) ;
// System.out.print("翻转后的链表 为 : ");
// while ( f != null) {
// System.out.print (f.val + " -- > ");
// f = f.next;
// }
pre = head;
// 现在开始 合并 链表
ListNode nextP = null;
ListNode nextF = null;
while (pre != null && f != null) {
nextP = pre.next;
nextF = f.next;
pre.next = f;
pre = nextP;
f.next = pre;
f = nextF;
}
}
// 类似 翻转链表 2 的 题目 code
private ListNode reserve(ListNode head , ListNode l , ListNode r) {
// r 是否走到尾部 、 没有走到尾部, 让他遍历到尾部
// 这个函数的目的是 翻转 [l,r] 区间上的所有 节点
for (; r.next != null; r = r.next);
ListNode next = null;
ListNode pre = null;
while ( l != null ) {
next = l.next;
l.next = pre;
pre = l;
l = next;
}
return pre;
}
}
合并两个有序链表
- 思路 : 这个题比较简单 双指针 分别指向两个链表 合并即可。
code
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 == null ) return list2;
if (list2 == null ) return list1;
ListNode p1 = list1;
ListNode p2 = list2;
// 不知道谁的头小 避免复杂的逻辑判断 开辟一个新的 dump 节点
ListNode dump = new ListNode();
ListNode p = dump;
while (p1 != null && p2 != null) {
if (p1.val <= p2.val) {
p.next = p1;
p1 = p1.next;
}else {
p.next = p2;
p2 = p2.next;
}
p = p.next;
}
if (p1 != null) p.next = p1;
if (p2 != null) p.next = p2;
return dump.next;
}
}
相交链表
- 思路 :想象两根绳子 打结 、 打结处 拎起来 打结点 到 两个绳子的某一处一定是长度相等的。 利用这个思路 。 求出长短差 长绳子走掉 相差的路程,与短绳子一起奔向节点。如果具有这样的节点 两个节点一定同时到达 判断 哈希地址是否相等即可。
code
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
int lthA = 0;
int lthB = 0;
ListNode pA = headA;
ListNode pB = headB;
while (pA != null) {
lthA ++;
pA = pA.next;
}
while (pB != null) {
lthB ++;
pB = pB.next;
}
pA = headA;
pB = headB;
int diff = 0;
if (lthA > lthB) {
diff = lthA - lthB;
while (diff -- > 0) {
pA = pA.next;
}
}else {
diff = lthB - lthA;
while (diff -- > 0) {
pB = pB.next;
}
}
while (pA != null && pB != null) {
// 有可能 刚巧 走完diff 就相遇了 就是这么巧
if (pA == pB) return pA;
pA = pA.next;
pB = pB.next;
}
return null;
}
}
一篇简洁的代码 coding 感觉原理上部分code 相同 但是不太好想
labuladong 题解地址
环形链表
- 思路: 快慢指针法 快指针两步 、 慢指针 一步、 快指针相对于 慢指针永远快一步、也就是说 可以想象为 在 慢指针到达入环点 如果是环的话, 可以想象为 慢指针站那不动啦,快指针一步一步去找他相遇。
- 细节 要保证 f 不为空 进入循环 保证循环的可行性。f.next.next 的 存在条件是 f.next 不为空。
code
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode s = head;
ListNode f = head.next.next;
while (f != null && f.next != null) {
if (s == f) return true;
s = s.next;
f = f.next.next;
}
return false;
}
}