双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个指针(特殊情况甚至可以多个),两个指针或是同方向访问两个链表、或是同方向访问一个链表(快慢指针)、或是相反方向扫描(对撞指针),从而达到我们需要的目的。
首先我们先来看一个简单的算法应用题,以下例题皆来自牛客网牛客网在线编程_算法篇_面试必刷TOP101 (nowcoder.com):
合并K个排序的链表
描述
合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
数据范围:节点总数 0≤𝑛≤50000≤n≤5000,每个节点的val满足 ∣𝑣𝑎𝑙∣<=1000∣val∣<=1000
要求:时间复杂度 𝑂(𝑛𝑙𝑜𝑔𝑛)
解题思路:
如果是两个有序链表合并,我们可能会利用归并排序合并阶段的思想:准备双指针分别放在两个链表头,每次取出较小的一个元素加入新的大链表,将其指针后移,继续比较,这样我们出去的都是最小的元素,自然就完成了排序。
这道题我们同样可以采取这种思想,先取出最前面的两个链表进行合并,在与后面的进行合并,如此循环下去便可得到一个最终的链表,但这样比较费时。
既然都是归并排序的思想了,那我们可不可以直接归并的分治来做,而不是顺序遍历合并链表呢?答案是可以的!
归并排序是什么?简单来说就是将一个数组每次划分成等长的两部分,对两部分进行排序即是子问题。对子问题继续划分,直到子问题只有1个元素。还原的时候呢,将每个子问题和它相邻的另一个子问题利用上述双指针的方式,1个与1个合并成2个,2个与2个合并成4个,因为这每个单独的子问题合并好的都是有序的,直到合并成原本长度的数组。
对于这k个链表,就相当于上述合并阶段的k个子问题,需要划分为链表数量更少的子问题,直到每一组合并时是两两合并,然后继续往上合并,这个过程基于递归:
- 终止条件: 划分的时候直到左右区间相等或左边大于右边。
- 返回值: 每级返回已经合并好的子问题链表。
- 本级任务: 对半划分,将划分后的子问题合并成新的链表。
具体做法:
- step 1:从链表数组的首和尾开始,每次划分从中间开始划分,划分成两半,得到左边𝑛/2n/2个链表和右边𝑛/2n/2个链表。
- step 2:继续不断递归划分,直到每部分链表数为1.
- step 3:将划分好的相邻两部分链表,按照两个有序链表合并的方式合并,合并好的两部分继续往上合并,直到最终合并成一个链表。
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param lists ListNode类ArrayList
* @return ListNode类
*/
public ListNode mergeKLists (ArrayList<ListNode> lists) {
// write code here
return divide(lists,0,lists.size()-1);
}
public ListNode Merge(ListNode head1,ListNode head2){
//加一个表头
ListNode head = new ListNode(-1);
ListNode cur = head;
//两个链表都要不为空
while(head1!=null&&head2!=null){
//取较小值的节点
if(head1.val<head2.val){
cur.next=head1;
//只移动取值的指针
head1=head1.next;
}else{
cur.next=head2;
//只移动取值的指针
head2=head2.next;
}
cur=cur.next;
}
//哪个链表还有剩,直接连在后面
if(head1!=null){
cur.next=head1;
}else{
cur.next=head2;
}
//返回值去掉表头
return head.next;
}
//划分合并区间函数
public ListNode divide(ArrayList<ListNode> lists,int left,int right){
if(left>right){
return null;
//中间一个的情况
}else if(left==right){
return lists.get(left);
}
//从中间分成两段,再将合并好的两段合并
int mid = (left+right)/2;
return Merge(divide(lists,left,mid),divide(lists,mid+1,right));
}
}
644

被折叠的 条评论
为什么被折叠?



