160. 相交链表
题解:
// 双指针遍历
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA, pB = headB;
// 分别遍历两个链表
// 如果遍历完当前链表,则指针指向另一个链表的头
// 知道两个指针相遇
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
// hash表存储
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> visited = new HashSet<ListNode>();
ListNode temp = headA;
// 首先遍历链表 headA,并将链表 headA 中的每个节点加入哈希集合中。
// 然后遍历链表 headB,对于遍历到的每个节点,判断该节点是否在哈希集合中:
while (temp != null) {
visited.add(temp);
temp = temp.next;
}
temp = headB;
while (temp != null) {
// 如果当前节点不在哈希集合中,则继续遍历下一个节点;
// 如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分
// 因此在链表 headB 中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。
if (visited.contains(temp)) {
return temp;
}
temp = temp.next;
}
return null;
}
142. 环形链表 II
题解:
/**
* 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) { // 快慢指针
// 如果链表是null,或者只有一个就没有环,直接返回null
if (head == null || head.next == null){
return null;
}
ListNode i = head; // 设置慢节点
ListNode j = head; // 设置快节点
// 循环遍历找重合点
while (true) {
// 如果快指针遍历结束,证明没有环
if (j.next == null || j.next.next == null) {
return null;
}
i = i.next; // 慢指针一次移动一位
j = j.next.next; // 快指针一次移动两位
if (i == j) { // 如果快慢指针重合,证明有环
break;
}
}
// 将慢指针重新指向头结点
i = head;
// j指针 位置不变 ,将i指针重新 指向链表头部节点 ;i和j同时每轮向前走n步; 此时f=0,s=nb;
// 当i指针走到f=a步时,j指针走到步s=a+nb,此时 两指针重合,并同时指向链表环入口 。
// 重新循环,返回索引为 1 (pos = 1)的链表节点(链表环入口)
while (i != j) {
i = i.next;
j = j.next;
}
return i;
}
public ListNode detectCycle(ListNode head) { // hash表
// 如果链表是null,或者只有一个就没有环,直接返回null
if (head == null || head.next == null) {
return null;
}
Set set = new HashSet<ListNode>();
ListNode i = head;
// 遍历链表,遍历的值如果存在hash表中,则证明有环
while (i != null) {
if (set.contains(i)) {
return i;
} else {
set.add(i);
}
i = i.next;
}
return null;
}
}
23. 合并K个升序链表
思考:虽然是困难题,但是他的暴力解法并不难想,首先把所有链表放在数组中,对数组排序再转成链表即可;
题解:
// K指针:K 个指针分别指向 K 条链表
// 每次 O(K) 比较 K个指针求 min, 时间复杂度:O(NK)
public ListNode mergeKLists(ListNode[] lists) {
int k = lists.length;
ListNode dummyHead = new ListNode(0);
ListNode tail = dummyHead;
while (true) {
ListNode minNode = null;
int minPointer = -1;
for (int i = 0; i < k; i++) {
if (lists[i] == null) {
continue;
}
if (minNode == null || lists[i].val < minNode.val) {
minNode = lists[i];
minPointer = i;
}
}
if (minPointer == -1) {
break;
}
tail.next = minNode;
tail = tail.next;
lists[minPointer] = lists[minPointer].next;
}
return dummyHead.next;
}
// 我们可以想到一种最朴素的方法
// 用一个变量 ans 来维护以及合并的链表
// 第 i 次循环把第 i 个链表和 ans 合并,答案保存到 ans 中。
public ListNode mergeKLists(ListNode[] lists) {
ListNode ans = null;
for (int i = 0; i < lists.length; ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
public ListNode mergeTwoLists(ListNode a, ListNode b) {
if (a == null || b == null) {
return a != null ? a : b;
}
ListNode head = new ListNode(0);
ListNode tail = head, aPtr = a, bPtr = b;
while (aPtr != null && bPtr != null) {
if (aPtr.val < bPtr.val) {
tail.next = aPtr;
aPtr = aPtr.next;
} else {
tail.next = bPtr;
bPtr = bPtr.next;
}
tail = tail.next;
}
tail.next = (aPtr != null ? aPtr : bPtr);
return head.next;
}