剑指 Offer 22. 链表中倒数第k个节点
剑指 Offer 25. 合并两个排序的链表
剑指 Offer 52. 两个链表的第一个公共节点
双指针考点:
1. 判断何时需要使用双指针,是否满足使用条件,如果太简单其实也没必要new出来俩指针强行用
2. 数组和链表的用法还是有些区别的,数组里面用双指针也可以,也很方便;但是链表里用的话更加原汁原味,更有感觉
3. 找准双指针如何设计,从哪个方向开始推进也很重要
题目一:
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。 返回删除后的链表的头节点。 注意:此题对比原题有改动 示例 1: 输入: head = [4,5,1,9], val = 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 示例 2: 输入: head = [4,5,1,9], val = 1 输出: [4,5,9] 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
解法一:很传统的一个题,prev为Head,cur为Head的下一个节点往后倒就行
public ListNode deleteNode(ListNode head, int val) {
if (head.val==val)return head.next;
ListNode prev = head;
ListNode cur = prev.next;
while (cur!=null && cur.val!=val){
prev = cur;
cur = cur.next;
}
if (cur!=null) prev.next = cur.next;
return head;
}
题目二:
链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。 例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。 示例: 给定一个链表: 1->2->3->4->5, 和 k = 2. 5 返回链表 4->5. 1 2 3 4 5 6 k 2
解法一:这块儿一个指针就够了,重点在于翻转K值,有点儿数学概念吧,也不难
public ListNode getKthFromEnd2(ListNode head, int k) {
if (head == null || head.next == null) return head;
int len = 0;
ListNode interval = head;
while (interval.next != null) {
len += 1;
interval = interval.next;
}
for (int i = 1; i <= len - k + 1; i++) {
head = head.next;
}
return head;
}
题目三:
合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。 示例1: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 限制: 0 <= 链表长度 <= 1000
解法一:就是一个比大小的问题,左大右小那就动右边,反之
public ListNode mergeTwoLists2(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
else if (l2 == null) return l1;
else if (l1.val > l2.val) {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
} else {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
}
题目四:
两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。 如下面的两个链表: 在节点 c1 开始相交。 示例 1: 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Reference of the node with value = 8 输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。 示例 2: 输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Reference of the node with value = 2 输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。 示例 3: 输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解法一:标准的双指针用法,直接垫上指针,然后while循环,不为null就下一个为null就走对方
ListNode getIntersectionNode2(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;
}
解法二:直接用HashSet,之后判断是否包含也不失为一良策
ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();
ListNode n = headA;
while (n!=null){
set.add(n);
n = n.next;
}
n = headB;
while (n!=null){
if (set.contains(n)){
return n;
}
n = n.next;
}
return null;
}