前言
有序链表的合并这一系列的问题在面试中也比较常见,因此总结下相关的问题
- NC33 合并有序链表(简单)
- NC51 合并k个已排序的链表(较难)
- NC70 单链表的排序(简单)
合并有序链表
描述: 将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的,且合并后新链表依然有序。
思路分析:创建一个虚拟的头节点, 然后遍历两个有序的链表,把val 小的节点作为当前节点的下一节点, 一直遍历到任意链表为空为止, 最后接上为遍历完的链表
AC 代码:
public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
ListNode ans = new ListNode(-1);
ListNode cur = ans;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
cur.next = l1;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 == null ? l2 : l1;
return ans.next;
}
合并k个已排序的链表
描述:合并 k 个已排序的链表并将其作为一个已排序的链表返回 思路分析:
-
借用合并有序链表中的 mergeTwoLists 方法, 将k个链表加入到队列中, 当队列的大小大于1时,取出2个链表合并,合并之后再添加到链表中, 循环直到队列大小为1
-
使用优先队列, 建一个小根堆, 把头节点放进去, 每次堆顶为值最小的节点,依次取出,然后再将它的下一个节点放回去,循环此过程
AC 代码:
遍历合并
public ListNode mergeKLists(ArrayList<ListNode> lists) {
Queue<ListNode> queue = new LinkedList<>();
for(ListNode node : lists) queue.offer(node);
while(queue.size() != 1){
ListNode l1 = queue.poll();
ListNode l2 = queue.poll();
queue.offer(mergeTwoLists(l1, l2));
}
return queue.poll();
}
使用优先队列的版本
public ListNode mergeKLists(ArrayList<ListNode> lists) {
Queue<ListNode> queue = new PriorityQueue<>((a,b)-> a.val - b.val);
ListNode ans = new ListNode(-1);
ListNode cur = ans;
for(ListNode node : lists){
if(node != null){
queue.offer(node);
}
}
while(!queue.isEmpty()){
ListNode node = queue.poll();
cur.next = node;
cur = cur.next;
if(node.next != null){
queue.offer(node.next);
}
}
return ans.next;
}
单链表的排序
描述: 给定一个无序单链表,实现单链表的排序(按升序排序)。
思路分析:利用快慢指针找到链表的中间节点, 然后将链表划分为两部分,递归直到只有一个节点, 然后自低向上排序归并, 借用链表合并中的 mergeTwoLists 方法
tip : 只有两个节点的使用划分问题 因为将中间的节点划分到了前一部分
AC 代码:
public ListNode sortInList (ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode slow = head;
ListNode fast = head.next;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode rightStart = slow.next;
slow.next = null;
ListNode leftSort = sortInList(head);
ListNode rightSort = sortInList(rightStart);
return mergeTwoLists(leftSort, rightSort);
}