字節面試算法真題之合併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的題解(不是個人原創),具體可以去查詢我上面給出的鏈接。數組的實現是參考鏈表的實現,實現隊列中存放的是一維的數組,和鏈表中實現存放的是一個單向鏈表思想一樣,就是對於數組的操作可能不向鏈表簡約,需要從新開闢新的空間數組來存放之前的剩餘,可能會定義一些新的變量出來,但是相比暴力合併數組來說思想還是更好,也更值得學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章