字节面试算法真题之合并n个数组/链表

前言

此题是自己在整理算法题目时候发现字节的面试官经常会问道这个题目,这里就做一个记录。
首先来看一下对应的leetcode:

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

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

思考

合并链表

首先这个题目是对于合并两个排序链表或者是说排序的数组的一个变形处理,这里就不讲述具体的合并两个的行为。关于具体的代码可以参见解体答案,这里利用到了优先队列:
来看具体的实现:
首先我们利用到优先队列:

 Queue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                return o1.val-o2.val;
            }
        });
        // 表示在使用到poll()时候都会抛出当前链表头节点的最小值。
public ListNode mergeKLists(ListNode[] lists) {
        Queue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                return o1.val-o2.val;
            }
        });
        for (ListNode node: lists) {
            if (node != null) {
                pq.offer(node);
            }
        }
        ListNode dummyHead = new ListNode(0);
        ListNode tail = dummyHead;
        // 先将当前链表头节点的最小值的链表抛出,然后取到当前最小值,并且完成之后,在当前链表不为空的时候继续存入到队列中。
        while (!pq.isEmpty()) {
            ListNode minNode = pq.poll();
            tail.next = minNode;
            tail = minNode;
            if (minNode.next != null) {
                pq.offer(minNode.next);
            }
        }
        return dummyHead.next;
    }

合并数组

有了以上的具体学习,然后在网上找了一下发现对应的合并数组的可能就时间复杂度很高,所以这里参考以上的代码完成以下的情况。

返回ArrayList

若是我们不考虑返回的情况,可以使用到ArrayList进行接收处理:

 private  static  ArrayList<Integer> resArray(int [][]num){
        int h=num.length;
        ArrayList<Integer> arrayList=new ArrayList<>();
        PriorityQueue<int [] > maxHeap=new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0]-o2[0];
            }
        });
        for(int i=0;i<num.length;i++){
            maxHeap.offer(num[i]);
        }
        while(!maxHeap.isEmpty()){
        // 定义一个数组,来对抛出的数组进行接收处理
            int []temp=maxHeap.poll();
            int []p=new int [temp.length-1];// 因为数组的第一个元素会被取出,所以这里新生成的数组的长度要减一处理。
            arrayList.add(temp[0]);
            int w=0;
            // 将剩余的放到新数组中存放到优先队列中。
            for(int i=1;i<temp.length;i++){
                p[w++]=temp[i];
            }
            if (p.length!=0)
            maxHeap.offer(p);
        }

        return  arrayList;
    }

返回int数组

思考:这里为什么返回一个array。其实也是可以返回一个数组,但是因为对于链表而言,是动态变化的长度,但是对于数组而言长度是固定不变的,若是题目要求说合并K个长度相同的数组,我们就可以这样写:因为对于每个数组的长度都是固定的,所以就可以说是新数组的长度就是原来二维数组的长和宽的乘积。

 private  static  int [] resInt(int [][]num){
     int h=num.length;
     int l=num[0].length;
     int []res=new int[h*l];

     PriorityQueue<int [] > maxHeap=new PriorityQueue<>(new Comparator<int[]>() {
         @Override
         public int compare(int[] o1, int[] o2) {
             return o1[0]-o2[0];
         }
     });
     for(int i=0;i<num.length;i++){
         maxHeap.offer(num[i]);
     }
     int j=0;
     while(!maxHeap.isEmpty()){
         int []temp=maxHeap.poll();
         int []p=new int [temp.length-1];
         res[j++]=temp[0];
         int w=0;
         for(int i=1;i<temp.length;i++){
             p[w++]=temp[i];
         }
         if (p.length!=0)
         maxHeap.offer(p);
     }
     return  res;
 }

但是若是说给定我们的数组的长度不是相同的,就需要我们计算出给定的数组中长度最长的是哪一个,然后利用最长的长度与个数相乘。但是这样就会出现一个问题,会出现后缀0,因为对于有的数组的长度可能不是最长的长度,就会导致新数组的长度要溢出,这个时候就需要做一个消除后缀0的操作,具体这部分的代码这里就不贴出,计算最长数组和消除后缀0都不是很复杂。

后记

链表的具体实现是别人leetcode的题解(不是个人原创),具体可以去查询我上面给出的链接。数组的实现是参考链表的实现,实现队列中存放的是一维的数组,和链表中实现存放的是一个单向链表思想一样,就是对于数组的操作可能不向链表简约,需要从新开辟新的空间数组来存放之前的剩余,可能会定义一些新的变量出来,但是相比暴力合并数组来说思想还是更好,也更值得学习。

猜你喜欢

转载自blog.csdn.net/weixin_44015043/article/details/106742147
今日推荐