链表合并| 8月更文挑战

391 阅读1分钟

前言

有序链表的合并这一系列的问题在面试中也比较常见,因此总结下相关的问题

  1. NC33 合并有序链表(简单)
  2. NC51 合并k个已排序的链表(较难)
  3. 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);
    }