線性時間的排序算法--桶排序(以leetcode164. Maximum Gap為例講解)


前言

   在比較排序的算法中,快速排序的性能最佳,時間復雜度是O(N*logN).因此,在使用比較排序時,時間復雜度的下限就是O(N*logN)。而桶排序的時間復雜度是O(N+C),因為它的實現並不是基於比較實現的,而是基於映射函數實現的。

桶排序

    桶排序工作的原理是將數組分到有限數量的桶子里。每個桶子再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。

   桶排序利用函數的映射關系,減少了幾乎所有的比較工作。實際上,桶排序的f(k)值的計算,其作用就相當於快排中划分,已經把大量數據分割成了基本有序的數據塊(桶)。然后只需要對桶中的少量數據做先進的比較排序即可。

時間復雜度和空間復雜度分析

    對於N個待排數據,M個桶,平均每個桶[N/M]個數據的桶排序平均時間復雜度為:
               O(N)+O(M*(N/M)*log(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM)
    當N=M時,即極限情況下每個桶只有一個數據時。桶排序的最好效率能夠達到O(N)。
 
    空間負載度:O(N+M)
 
下面以leetcode164. Maximum Gap為例具體講解桶排序的實現
   題目要求:
   Given an unsorted array, find the maximum difference between the successive elements in its sorted form.

   Try to solve it in linear time/space.  //題目要求我們使用線性的時間

   Return 0 if the array contains less than 2 elements.

   You may assume all elements in the array are non-negative integers and fit in the 32-bit signed integer range.

解題思路:

  首先對數組進行桶排序,然后找出最大的gap。

  首先明確,最大的gap肯定是后桶的最小值減去前桶的最大值,注意此處說的桶均為有效桶,即不包括空桶。

  因此無需進行桶內排序,僅需記錄每個有效桶的最大值和最小值即可。

  為何最大的gap肯定是后桶的最小值減去前桶的最大值?

  假設有數組x[1...n],其中最大的元素為max,最小元素為min,將左閉右開的實數區間[min,max)划分為n-1個等長的子區間(桶),每個子區間也是左閉右開的,我們用len來表示每個子區間的長度。除去max和min,剩下的n-2個數,每個數都屬於其中一個桶。對於同一個桶的兩個數,因為桶是左閉右開的,所以他們的距離肯定是小於len的。然后,關鍵的一點是,n-2個數放進n-1個桶,由抽屜原理可以知道,肯定有一個桶是空的,所以,距離最遠的相鄰的兩個數,肯定是屬於兩個不同的桶。於是,我們可以把每個桶都掃描一次,相鄰最遠的兩個數,必定其中一個是某個桶里的最大值,另一個是另一個桶里的最小值。

 

代碼:

public class Solution {
  public int maximumGap(int[] num) {
      if(num == null || num.length<2){
          return 0;
      }
      int maxval = Integer.MIN_VALUE;
      int minval = Integer.MAX_VALUE;
      //求解數組最值
      for(int i=0; i<num.length; i++){
          if(num[i]>maxval) maxval = num[i];
          if(num[i]<minval) minval = num[i];
      }
      
      //數組內的元素值相同
      if(minval==maxval){
          return 0;
      }
      
      //數組僅有兩個元素
      if(num.length==2){
          return maxval-minval;
      }
      
      int len = (int)Math.ceil((double)(maxval-minval)/(num.length-1));  //求解桶間差值,向上取整
      int n = (maxval-minval)/len;
      
      int maxBuk[] = new int[n+1];
      int minBuk[] = new int[n+1];
      
      Arrays.fill(maxBuk,Integer.MIN_VALUE);
      Arrays.fill(minBuk,Integer.MAX_VALUE);
      
      //桶映射
      for(int val:num){
          int temp = (val-minval)/len;
          maxBuk[temp] = Math.max(val,maxBuk[temp]);
          minBuk[temp] = Math.min(val,minBuk[temp]);
      }
      
      //求解最大gap,最大差值位於后桶的min-前桶的max
      int gap = 0;
      int pre = maxBuk[0];
      for(int i=1; i<=n; i++){
          if(maxBuk[i]==Integer.MIN_VALUE && minBuk[i]==Integer.MAX_VALUE){  //忽略空桶
              continue;
          }
          gap = Math.max(gap,minBuk[i]-pre);
          pre = maxBuk[i];
      }
      
      return gap;
      
      
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM