K路归并-简单选择(Java)
核心思想: 对于K个升序链表中的结点,利用简单选择排序算法的思想,即从还有结点尚未合并的链表中顺序比较头结点的val并标记最小值结点,每次最多从K个链表中选择一个最小值结点并将其合并到结果链表resList中,直至K个链表的所有结点合并完成。
/**
* 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) {
ListNode resList = new ListNode(0,null);//结果链表
ListNode rear = resList;//尾插法建表
while(true){
ListNode min = null;//用于指向最小值结点
int minPos = -1;//用于标记最小值结点下标
//从还有结点尚未合并的链表中顺序比较头结点的val并标记最小值结点
for(int i = 0; i < lists.length; i++){
if(lists[i] == null)//跳过已合并完成的链表
continue;
if(min == null || min.val > lists[i].val){//更新最小值结点
min = lists[i];
minPos = i;
}
}
if(minPos == -1)//K个链表的所有结点合并完成
break;
lists[minPos] = lists[minPos].next;//在原链表中删除最小值结点
min.next = rear.next;
rear.next = min;//最小值结点合并到结果链表
rear = min;
}
return resList.next;
}
}
K路归并-小根堆(Java)
核心思想: 对上述采用简单选择排序算法的思想进行优化,改用堆(小根堆)排序算法思想,Java优先队列的底层实现正是基于小根堆。
/**
* 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) {
ListNode resList = new ListNode(0, null);//结果链表
ListNode rear = resList;//尾插法建表
PriorityQueue<ListNode> pq = new PriorityQueue<>((node1, node2) -> node1.val - node2.val);//优先队列
//将链表数组中所有链表的非空头结点加入优先队列
for(int i = 0; i < lists.length; i++){
if(lists[i] != null)
pq.offer(lists[i]);
}
while(pq.size() != 0){//优先队列不空,即还有结点尚未合并的链表
ListNode min = pq.poll();//指向最小值结点
if(min.next != null)
pq.offer(min.next);
min.next = rear.next;
rear.next = min;//最小值结点合并到结果链表
rear = min;
}
return resList.next;
}
}
两两归并-顺序
核心思想: 将链表数组中的前两个链表进行归并,之后再将归并后的结果链表和链表数组中的第三个链表进行归并,依次归并直至K个链表归并完成。
/**
* 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) {
ListNode resList = null;//结果链表
//顺序归并链表数组的每个链表
for(int i = 0; i < lists.length; i++)
resList = mergeTwoLists(resList, lists[i]);
return resList;
}
//合并两个有序链表
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null)//list1合并完成
return list2;
else if(list2 == null)//list2合并完成
return list1;
else if(list1.val < list2.val){//list1的结点值比list2的结点值小
//较小值的结点和较小值结点所在链表list1的剩余结点与另一链表list2的所有结点合并的结果进行合并
list1.next = mergeTwoLists(list1.next, list2);
return list1;
}
else{
//较小值的结点和较小值结点所在链表list2的剩余结点与另一链表list1的所有结点合并的结果进行合并
list2.next = mergeTwoLists(list2.next, list1);
return list2;
}
}
}
两两归并-分治
核心思想:
/**
* 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 == null || lists.length == 0)//特判
return null;
return merge(lists, 0, lists.length - 1);
}
//分治归并链表数组的每个链表
public ListNode merge(ListNode[] lists, int left, int right){
if(left == right)
return lists[left];
int mid = (left + right) / 2;
ListNode leftList = merge(lists, left, mid);
ListNode rightList = merge(lists, mid + 1, right);
return mergeTwoLists(leftList, rightList);
}
//合并两个有序链表
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null)//list1合并完成
return list2;
else if(list2 == null)//list2合并完成
return list1;
else if(list1.val < list2.val){//list1的结点值比list2的结点值小
//较小值的结点和较小值结点所在链表list1的剩余结点与另一链表list2的所有结点合并的结果进行合并
list1.next = mergeTwoLists(list1.next, list2);
return list1;
}
else{
//较小值的结点和较小值结点所在链表list2的剩余结点与另一链表list1的所有结点合并的结果进行合并
list2.next = mergeTwoLists(list2.next, list1);
return list2;
}
}
}