LeetCode 23.合并K个排序链表

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 

详解:

将K个排序链表合并成一个,假设合并之后的总长度是n。

我们以前经常说讲两个排序列表合并成一个排序列表(他的长度为n),时间复杂度为o(n)

那这个题目变成了k个,如果还是按照原来的方法,可以看到,每一次的计算都要遍历k个数组拿到最小值,复杂度就是o(n*k),显然慢了。那怎么优化呢?

深入一点理解刚才的思路,就是先从k个列表的第一个数,找出最小数,加入新列表,然后从这个最小数所在列表的第二个数和其他k-1个列表,在找出最小的数,加入新列表,以此类推。

是不是听着听着,感觉似曾相识?找最小,更新他的值,再找最小,以此类推。

对的,没错,不用怕,哪怕是链表,依然甩出宝刀,线段树。

我擦,这是链表阿,怎么建树?

对k建树,进行n次查询和更新即可。

然后看看时间复杂度:o(n*logk)

这样去理解这就是典型的线段树了,只是插入和更新都是操作的链表节点,编码不能马虎,不然容易出bug。

至于线段树的原理,这里就不详说了,下次有机会再开一贴详解,或者自行去搜。

再详细的思路也不及代码片行,那直接贴代码:

    private Node[] nodeList;

    public ListNode mergeKLists(ListNode[] lists) {
        int length = lists.length;
        if(length < 1){
            return null;
        }
        nodeList = new Node[length << 2];
        build(1, 0, length - 1, lists);
        ListNode result = null, tmp = null;
        while(lists[nodeList[1].k] != null){
            if(result == null){
                result = lists[nodeList[1].k];
                lists[nodeList[1].k] = lists[nodeList[1].k].next;
                tmp = result;
                result.next = null;
            }else{
                tmp.next = lists[nodeList[1].k];
                lists[nodeList[1].k] = lists[nodeList[1].k].next;
                tmp = tmp.next;
                tmp.next = null;
            }
            change(1, nodeList[1].k, (lists[nodeList[1].k] == null) ? Integer.MAX_VALUE : lists[nodeList[1].k].val);
        }
        return result;
    }

  public class Node{
        int left;
        int right;
        int minValue;
        int k;
  }

    public void update(int n){
        if(nodeList[n << 1].minValue < nodeList[(n << 1) + 1].minValue){
            nodeList[n].minValue = nodeList[n << 1].minValue;
            nodeList[n].k = nodeList[n << 1].k;
        }else{
            nodeList[n].minValue = nodeList[(n << 1) + 1].minValue;
            nodeList[n].k = nodeList[(n << 1) + 1].k;
        }
    }

  public void build(int n, int left, int right, ListNode[] lists){
        Node node = new Node();
        nodeList[n] = node;
        node.left = left;
        node.right = right;
        if(left == right){
            if(lists[left] != null){
                node.minValue = lists[left].val;
            }else{
                node.minValue = Integer.MAX_VALUE;
            }
            node.k = left;
            return;
        }
        int mid = (left + right) >> 1;
        build(n << 1, left, mid, lists);
        build((n << 1) + 1, mid + 1, right, lists);
        update(n);
  }

  public void change(int n, int k, int value){
        if(nodeList[n].left == nodeList[n].right){
            nodeList[n].minValue = value;
            nodeList[n].k = k;
            return;
        }
      int mid = (nodeList[n].left + nodeList[n].right) >> 1;
      if(k <= mid){
          change(n << 1, k, value);
      }else{
          change((n << 1) + 1, k, value);
      }
      update(n);
  }

最后贴效率:

写在后面:

这个是我的思路,有其他的思路也欢迎留言学习,毕竟算法无界,可能还有更优的算法也未尝不可能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值