Leecode23_合并K个排序链表_PriorityQueue_两两merge_暴力

在这里插入图片描述

思路一:暴力法解决

1.先将数组中所有的list中的节点全部转存到ArrayList中
2.通过Collections.sort对数组中的元素进行排序
3.根据数组中元素个数,边遍历边重新生成ListNode

第一步遍历时间复杂度O(n),空间复杂度O(n)
第二步进行排序时间复杂度O(nlogn),空间复杂度取决于算法选择
第三步遍历生成ListNode时间复杂度O(n),空间复杂度O(n)

综上:时间复杂度O(nlogn),空间复杂度O(n)

//暴力破解
      public ListNode mergeKLists(ListNode[] lists) {
        ListNode head = new ListNode(-1);
        ListNode next_p = head;
        //数据全部存入数组————排序————重新建list
        List<Integer> nums = new ArrayList<>();
       for (int i = 0; i<lists.length;i++){
            ListNode p = lists[i];
            while(p!=null){
                nums.add(p.val);
                p=p.next;
            }
       }
       Collections.sort(nums);
        while(!nums.isEmpty()){
           next_p.next = new ListNode(nums.remove(0));
           next_p = next_p.next;
       }
       return head.next;
    }

执行用时 :
14 ms, 在所有 Java 提交中击败了32.21%的用户
内存消耗 :
41.2 MB, 在所有 Java 提交中击败了71.61%的用户

思路二:将数组中list两两合并

两个链表的归并很容易完成。所以可以将list数组两个list进行合并,直到所有的list被合并完成。
时间复杂度:
merge的时间复杂度为O(n),n为两个链表的总长度
在此算法中
list0 list1 list2 list3 … listk-1
每两次归并实际上是前面所有的list的一次归并
list0 list1 l归并一次
list0 list1 list2 归并一次
list0 list1 list2 list3 归并一次

list0 list1 list2 list3…listk-1归并一次
所以
list0 遍历k-1次(k为list集合长度)
list1 遍历k-2次
listn遍历1一次

假设所有list中元素总个数为N,平均每个list中个数N/k,则其复杂度为:
(k-1)N/k+(k-2)N/k+…1N/k=((k*k-1)/2)*N/k=(N(k-1))/2
所以其时间复杂度为:O(kN)

    public ListNode mergeKLists(ListNode[] lists) {
        ListNode list = null;
        //list数组为空返回null
        if(lists.length==0 ) return list;
        //归并返回ListNode
        int i = 0;
        //将i和i+1的list归并,归并后存入i+1的位置用于下次继续合并
        for( ;i<lists.length-1;i++){
            lists[i+1]=merge(lists[i],lists[i+1]);
        }
        return lists[i];
    }
//merge
    private ListNode merge(ListNode list, ListNode list1) {
        ListNode re_list = new ListNode(-1);
        if(list==null&&list1==null)
            return re_list.next;
        ListNode p = re_list;
        while(list!=null&&list1!=null){
            if(list.val<list1.val){
                p.next = list;
                list = list.next;
            }else{
                p.next = list1;
                list1 = list1.next;
            }
            p=p.next;
        }
        if(list==null&&list1!=null)
            p.next = list1;
        if(list!=null&&list1==null)
            p.next = list;
        return re_list.next;
    }

执行用时 :
143 ms, 在所有 Java 提交中击败了21.21%的用户
内存消耗 :
41.4 MB, 在所有 Java 提交中击败了67.22%的用户

思路三:利用优先队列

1.将所有数值存入优先队列(比较器ListNode val值递增)
2.将优先队列输出,重新连接

构造器比较排序时间复杂度O(logk),k为list个数
所以总的时间复杂度O(nlogk)

 public ListNode mergeKLists(ListNode[] lists) {
        ListNode head = null;
        if(lists.length==0) return null;
            //先将所有的数据存入优先队列中
        Queue<ListNode> pq = new PriorityQueue<ListNode>(new Comparator<>() {
            @Override
            public int compare(ListNode listNode, ListNode t1) {
                return listNode.val-t1.val;
                }
        });
        for(int i = 0; i < lists.length; i++){
            ListNode p = lists[i];
            while(p!=null){
                pq.offer(p);
                p=p.next;
                }
        }
        ListNode dummyhead = new ListNode(-1);
        head = dummyhead;
        while(!pq.isEmpty()){
            head.next = pq.poll();
            head = head.next;
        }
        head.next = null;//因为head不为空,所以head.next不可能出现空指针议程
        return dummyhead.next;
    }

执行用时 :
9 ms, 在所有 Java 提交中击败了42.66%的用户
内存消耗 :
41.6 MB, 在所有 Java 提交中击败了63.32%的用户

可以看出有时候暴力解决问题,还是很好地嘛~

发布了16 篇原创文章 · 获赞 0 · 访问量 557

猜你喜欢

转载自blog.csdn.net/sunlili_yt/article/details/105288139