对于单链表相关的问题,双指针技巧是一种非常广泛且有效的解决方法。以下是一些常见问题以及使用双指针技巧解决
合并两个有序链表: 使用两个指针分别指向两个链表的头部,逐一比较节点的值,将较小的节点链接到结果链表中,直至其中一个链表遍历完毕。
链表的分解: 使用快慢指针技巧,快指针每次移动两步,慢指针每次移动一步,直到快指针到达链表尾部。这样可以找到链表的中间节点,从而将链表分解成两部分。
合并 k 个有序链表: 可以利用归并排序的思想,两两合并链表,直到合并成一个链表。
寻找单链表的倒数第 k 个节点: 使用两个指针,让一个指针先移动 k 步,然后两个指针一起移动,直到第一个指针到达链表尾部,此时第二个指针指向的节点即为倒数第 k 个节点。
寻找单链表的中点: 同样使用快慢指针技巧,快指针每次移动两步,慢指针每次移动一步,直到快指针到达链表尾部,慢指针即为中点。
判断单链表是否包含环并找出环起点: 使用快慢指针技巧,如果存在环,快指针最终会追上慢指针。找到相遇点后,将其中一个指针移动到链表头部,然后两个指针以相同速度移动,再次相遇的节点即为环的起点。
判断两个单链表是否相交并找出交点: 分别遍历两个链表,得到它们的长度差,然后让长链表的指针先移动长度差步数,接着两个链表同时遍历,直到找到相同的节点为止。
总的来说,双指针技巧在解决单链表相关问题时非常实用,它能够高效地解决许多常见问题,包括合并、分解、寻找节点、判断是否存在环等等。
一、合并两个有序列表
题目描述
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = [] 输出:[]
示例 3:
输入:l1 = [], l2 = [0] 输出:[0]
提示:
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
解题思路及代码
对两个链表进行比较,较小的再NewNode后面,NewNode向后移动;如果循环结束均为空,说明NewNode为合并后的链表;如果list1或list2其中一个为空,则NewNode连接的另一个链表就是合并后的链表
/**
* 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 mergeTwoLists(ListNode list1, ListNode list2) {
//定义哨兵
ListNode NewNode=new ListNode(-1);
ListNode head=NewNode;
//对两个链表进行比较,较小的再NewNode后面,NewNode向后移动。
//如果循环结束均为空,说明NewNode为合并后的链表
//如果list1或list2其中一个为空,则NewNode连接的另一个链表就是合并后的链表
while(list1 != null && list2!=null){
if(list1.val <= list2.val){
NewNode.next=new ListNode(list1.val);
NewNode=NewNode.next;
list1=list1.next;
}else{
NewNode.next=new ListNode(list2.val);
NewNode=NewNode.next;
list2=list2.next;
}
}
if(list1==null && list2==null){
return head.next;
}else if(list1==null){
NewNode.next=list2;
}else{
NewNode.next=list1;
}
return head.next;
}
}
结果展示
二、合并K个有序链表
这是一道困难的题目,题目给出K给有序链表,我们应该使用更加高效的方法(优先队列)来解决。
题目描述
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6
示例 2:
输入:lists = [] 输出:[]
示例 3:
输入:lists = [[]] 输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i]
按 升序 排列lists[i].length
的总和不超过10^4
解题思路及代码
/**
* 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 mergeKLists(ListNode[] lists) {
if(lists.length==0)return null;
ListNode dump=new ListNode(-1);
ListNode result=dump;
//定义优先队列 二叉堆
PriorityQueue<ListNode> pql=new PriorityQueue<>(lists.length,(a,b)->(a.val-b.val));
//将每个链表的头节点加入二叉堆中
for(ListNode head:lists){
if(head!=null){
pql.add(head);
}
}
//如果队列不为空,取出优先队列的最小值,加入新链表中,再将最小值的下一节点加入优先队列中
while(!pql.isEmpty()){
ListNode temp=pql.poll();
dump.next=temp;
if(temp.next!=null){
pql.add(temp.next);
}
dump=dump.next;
}
return result.next;
}
}