桶排序與力扣(LeetCode) -164 最大間距

在我的博客冒泡排序稿黄、插入排序喊衫、快速排序、堆排序杆怕、歸并排序總結(jié)中介紹了幾種經(jīng)典的排序方法族购,其中快速排序、堆排序和歸并排序的平均時間復(fù)雜度都是nlog(n)陵珍。下面我將會介紹另一種經(jīng)典的排序算法寝杖,平均時間復(fù)雜度可以達到o(n),但是有幾個限制條件互纯,我們來看一下瑟幕。

桶排序

首先我們來了解一下桶排序的思想。桶排序是體現(xiàn)了分治的思想留潦。原理是創(chuàng)建若干個桶只盹,通過某種映射將待排序的元素放置到桶中,使每個桶中的最大值都小于他后面一個桶的最小值兔院,并且每個桶容納的元素數(shù)量盡可能平均殖卑。然后對每個桶中的元素排序。最后將所有桶中的元素依次輸出坊萝。
下面兩副圖很好的解釋了桶排序的思想孵稽。


2018033115584192.gif
20180331155854638.png

有幾個關(guān)鍵的因素影響了桶排序的時間復(fù)雜度和空間復(fù)雜度

1.待排序元素的分布情況
如果我們要對{1,2屹堰,3肛冶,4,5扯键,90}進行排序睦袖,假設(shè)桶的個數(shù)就是元素的個數(shù),那么會出現(xiàn)第一個桶有5個元素荣刑,中間的桶都是空的馅笙,最后一個桶有一個元素。這樣厉亏,時間復(fù)雜度就退化成nlog(n)董习。
2.桶的個數(shù)
桶的個數(shù)越多,每個桶中的數(shù)量就越少爱只,每個桶內(nèi)進行排序的時間復(fù)雜度就約低皿淋。當(dāng)桶的個數(shù)滿足每個桶只裝一個元素時,桶排序所消耗的主要在為每個元素分配桶上面,為o(n)窝趣。

桶排序代碼實現(xiàn)

public class BucketSort {
    public int[] sort(int[] src,int bucketNum) {
        if(src==null||src.length<2)
            return src;
        int myBucketNum = bucketNum > src.length ? src.length : bucketNum;  //排除異常桶個數(shù)
        Bucket[] buckets = new Bucket[myBucketNum];             //創(chuàng)建桶
        int max = src[0];
        int min = src[0];
        for(int i=1;i<src.length;i++) {                    //尋找最大值和最小值
            if(src[i]>max)
                max=src[i];
            if(src[i]<min)
                min=src[i];
        }
        if(max==min)
            return src;
        int capacity = (int)Math.ceil((double)(max-min+1)/myBucketNum);   //計算每個桶的容量
        for(int i=0;i<src.length;i++) {                                   //遍歷每個元素疯暑,將元素分配到對應(yīng)桶中
            int bucketIndex = getBucketIndex(capacity,min,src[i]);
            putNumInBucket(buckets,bucketIndex,src[i]);
        }
        int[] result = new int[src.length];
        int resultIndex=0;
        for(int i=0;i<myBucketNum;i++) {                              //將所有桶中的數(shù)據(jù)合并
            if(buckets[i]!=null) {
                Node tempNode=buckets[i].node;
                while(tempNode!=null) {
                    result[resultIndex]=tempNode.value;
                    tempNode=tempNode.next;
                    resultIndex++;
                }
            }
        }
        return result;
    }
    /**
     * 獲得num所分配的桶的index
     * @param capacity
     * @param min
     * @param num
     * @return
     */
    private int getBucketIndex(int capacity,int min,int num) {
        return (num-min)/capacity;
    }
    /**
     * 將num分配到桶中,并使用插入排序?qū)ν皟?nèi)元素進行排序
     * @param buckets
     * @param index
     * @param num
     */
    private void putNumInBucket(Bucket[] buckets,int index,int num) {
        if(buckets[index]==null) {
            buckets[index] = new Bucket();
        }
        Node pre=null;
        Node cur=buckets[index].node;
        Node newNode = new Node();
        newNode.value = num;
        while(cur!=null&&cur.value<num) {
            pre=cur;
            cur=cur.next;
        }
        if(pre==null) {
            buckets[index].node=newNode;
            if(cur!=null)
                newNode.next=cur;
        }else {
            if(cur==null) {
                pre.next=newNode;
            }else {
                pre.next=newNode;
                newNode.next=cur;
            }
        }
    }
    /**
     * 桶
     * @author TinyMonster
     *
     */
    private class Bucket{
        Node node;
    }
    /**
     * 鏈表結(jié)點
     * @author TinyMonster
     *
     */
    private class Node{
        int value;
        Node next;
    }
    
    public static void main(String[] args) {
        BucketSort bucketSort = new BucketSort();
        int[] src = {4,1,7,2,9,3,0,6,5,8,11};
        int[] result = bucketSort.sort(src, 3);
        for(int i=0;i<result.length;i++) {
            System.out.println(result[i]);
        }
    }
}

時間復(fù)雜度分析

在桶排序中哑舒,我們首先遍歷所有元素妇拯,找出來最大值和最小值,這個操作的時間復(fù)雜度是o(n)洗鸵。
然后我們遍歷每個元素越锈,將元素分配到對應(yīng)的桶中,并將桶內(nèi)元素排序膘滨。假設(shè)元素總個數(shù)是N甘凭,桶的個數(shù)是M,元素都平均分布(最好情況)吏祸,使用nlog(n)的排序方法對每個桶內(nèi)元素進行排序对蒲,這個操作時間復(fù)雜度是M(N/M)log(N/M);當(dāng)M==N是贡翘,這個操作的時間復(fù)雜度是o(n)蹈矮。
最后我們遍歷所有元素,得到最終結(jié)果鸣驱,時間復(fù)雜度是o(n)泛鸟。
因此最好情況下的時間復(fù)雜度是o(n)。
但是當(dāng)元素分布極不均勻的情況下踊东,時間復(fù)雜度退化成nlog(n)北滥。

空間復(fù)雜度:
使用了鏈表來保存中間結(jié)果,空間復(fù)雜度是o(n)

(LeetCode) -164 最大間距

了解了桶排序的思想闸翅,我們來看一下下面一道題再芋,也是用桶排序的思想來解決的。
給定一個無序的數(shù)組坚冀,找出數(shù)組在排序之后济赎,相鄰元素之間最大的差值。
如果數(shù)組元素個數(shù)小于 2记某,則返回 0司训。
示例 1:
輸入: [3,6,9,1]
輸出: 3
解釋: 排序后的數(shù)組是 [1,3,6,9], 其中相鄰元素 (3,6) 和 (6,9) 之間都存在最大差值 3。
示例 2:
輸入: [10]
輸出: 0
解釋: 數(shù)組元素個數(shù)小于 2液南,因此返回 0壳猜。
說明:
你可以假設(shè)數(shù)組中所有元素都是非負整數(shù),且數(shù)值在 32 位有符號整數(shù)范圍內(nèi)滑凉。
請嘗試在線性時間復(fù)雜度和空間復(fù)雜度的條件下解決此問題统扳。

解題思路

題目要求在線性時間復(fù)雜度和空間復(fù)雜度的前提下找到相鄰元素的最大差喘帚。
我們可以先對元素進行排序,為了達到線性時間復(fù)雜度闪幽,我們采用桶排序(也可以使用計數(shù)排序啥辨,不過使用桶排序在該題中空間復(fù)雜度更小)盯腌。
我們可以很巧妙地設(shè)置每個桶的大小:假設(shè)所有元素的最大值為max,最小值為min陨瘩,元素個數(shù)是n腕够,則最大間距將不小于Math.ceil((max-min)/(n-1))。因此舌劳,我們設(shè)置桶的容量是Math.ceil((max-min)/(n-1))帚湘,則桶內(nèi)的元素的間距肯定不是最大間距,我們只需要查看桶間的間距(前一個桶的最大值和后一個桶的最小值的差)的最大值即可甚淡。
此外大诸,在桶內(nèi)并不需要保存每個元素,只需要保存該桶的最大值和最小值即可贯卦。
有了上面的思路资柔,我們可以寫出來下面的代碼

class Solution {
    public int maximumGap(int[] nums) {
        if(nums==null||nums.length<2)
            return 0;
        double min=nums[0];
        double max=nums[0];
        for(int i=0;i<nums.length;i++){       //遍歷所有元素,找到最大值和最小值
            min = nums[i]<min ? nums[i]:min;
            max = nums[i]>max ? nums[i]:max;
        }
        if(min==max)
            return 0;
        Bucket[] buckets=new Bucket[nums.length];
        int gap=(int)Math.ceil((max-min)/(nums.length-1));      //計算桶的容量
        for(int i=0;i<nums.length;i++){                         //遍歷每個元素撵割,計算該元素應(yīng)該放置的桶的位置贿堰,將元素放入桶中,更新桶的最大值和最小值
            int index=getBucketIndex((int)min,nums[i],gap);
            putInBucket(buckets,nums[i],index);
        }
        int maxGap=buckets[0].max-buckets[0].min;
        int pre=buckets[0].max;
        for(int i=1;i<buckets.length;i++){                   //遍歷所有桶啡彬,計算最大間距(桶間間距)
            if(buckets[i]!=null){
                if((buckets[i].min-pre)>maxGap){
                    maxGap=buckets[i].min-pre;
                }
                pre=buckets[i].max;
            }
        }
        return maxGap;
    }
    //內(nèi)部類 桶
    class Bucket{
        int max=0;
        int min=0;
        boolean hasNum=false;
    }
    //根據(jù)元素的數(shù)值計算該元素應(yīng)該在哪個桶中
    public int getBucketIndex(int min,int num,int gap){
        return (int)(num-min)/gap;
    }
    //將元素放入桶種羹与,更新桶的最大值和最小值
    public void putInBucket(Bucket[] buckets,int num,int index){
        if(buckets[index]==null){
            buckets[index]=new Bucket();
            buckets[index].hasNum=true;
            buckets[index].max=num;
            buckets[index].min=num;
        }
        if(num>buckets[index].max)
            buckets[index].max=num;
        if(num<buckets[index].min)
            buckets[index].min=num;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市庶灿,隨后出現(xiàn)的幾起案子纵搁,更是在濱河造成了極大的恐慌,老刑警劉巖往踢,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腾誉,死亡現(xiàn)場離奇詭異,居然都是意外死亡菲语,警方通過查閱死者的電腦和手機妄辩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來山上,“玉大人眼耀,你說我怎么就攤上這事∨搴叮” “怎么了哮伟?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵干花,是天一觀的道長。 經(jīng)常有香客問我楞黄,道長池凄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任鬼廓,我火速辦了婚禮肿仑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碎税。我一直安慰自己尤慰,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布雷蹂。 她就那樣靜靜地躺著伟端,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匪煌。 梳的紋絲不亂的頭發(fā)上责蝠,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機與錄音萎庭,去河邊找鬼霜医。 笑死,一個胖子當(dāng)著我的面吹牛擎椰,可吹牛的內(nèi)容都是我干的支子。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼达舒,長吁一口氣:“原來是場噩夢啊……” “哼值朋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起巩搏,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤昨登,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贯底,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丰辣,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年禽捆,在試婚紗的時候發(fā)現(xiàn)自己被綠了笙什。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡胚想,死狀恐怖琐凭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浊服,我是刑警寧澤统屈,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布胚吁,位于F島的核電站,受9級特大地震影響愁憔,放射性物質(zhì)發(fā)生泄漏腕扶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一吨掌、第九天 我趴在偏房一處隱蔽的房頂上張望半抱。 院中可真熱鬧,春花似錦膜宋、人聲如沸代虾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至江掩,卻和暖如春学辱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背环形。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工策泣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抬吟。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓萨咕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親火本。 傳聞我的和親對象是個殘疾皇子危队,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,494評論 2 348