LintCode 617. 最大平均值子數(shù)組

原題

第一步廉沮,萬年不變的查錯凹髓。如果給的array是null或空,那么直接return贫导。

    public double maxAverage(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        ...
    }

思路首先就是因?yàn)橐襰ubarray的最大平均值抛猫,肯定需要知道每一個(gè)subarray的和,所以肯定就得用prefix sum脱盲。先把prefix sum做出來邑滨。Prefix Sum就是把從0到i的和全部存在一個(gè)數(shù)組里日缨,這樣只要用sums[j] - sums[i]就可以知道從ij的subarray的和钱反。

        int n = nums.length;
        long[] sums = new long[n + 1];
        sums[0] = 0L;
        for (int i = 0; i < n; i++) {
            sums[i + 1] = sums[i] + nums[i];
        }

然后最簡單的做法就是Two Pointer遍歷打擂臺。找出所有大于k的字?jǐn)?shù)組的平均值匣距,然后return面哥。

        double maxAvg = Double.NEGATIVE_INFINITY;
        for (int i = 0; i < n - k + 1; i++) {
            for (int j = i + k; j < n + 1; j++) {
                long sum = sums[j] - sums[i];
                double avg = ((double) sum) / (j - i);
                maxAvg = Math.max(maxAvg, avg);
            }
        }

        return maxAvg;

這個(gè)做法可以過93%的測試。最后一個(gè)測試過不了是因?yàn)橹岛芏嘁愦莚ange很小尚卫,LintCode肯定是希望用二分法解的,所以時(shí)間卡的很小尸红。但是這種方法肯定能解出來吱涉。
完整的code

public class Solution {
    /*
     * @param nums: an array with positive and negative numbers
     * @param k: an integer
     * @return: the maximum average
     */
    public double maxAverage(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        int n = nums.length;
        long[] sums = new long[n + 1];
        sums[0] = 0L;
        for (int i = 0; i < n; i++) {
            sums[i + 1] = sums[i] + nums[i];
        }

        double maxAvg = Double.NEGATIVE_INFINITY;
        for (int i = 0; i < n - k + 1; i++) {
            for (int j = i + k; j < n + 1; j++) {
                long sum = sums[j] - sums[i];
                double avg = ((double) sum) / (j - i);
                maxAvg = Math.max(maxAvg, avg);
            }
        }

        return maxAvg;
    }
}

解2

二分法的解法,也得用prefix sum外里,但是用法不一樣怎爵。先找出最大值最小值,作為range的start 跟end盅蝗。

        double start = (double) nums[0];
        double end = (double) nums[0];
        for (int i = 0; i < nums.length; i++) {
            start = Math.min(start, nums[i]);
            end = Math.max(end, nums[i]);
        }

然后開始二分鳖链,找出中間數(shù),看有沒有一個(gè)平均值大于中間數(shù)的字?jǐn)?shù)組存在墩莫,如果有芙委,就把start放在中間,如果沒有狂秦,就把end放在中間灌侣,然后繼續(xù)二分。這里while的條件是end-start >= 1e-6裂问,因?yàn)閐ouble是有小數(shù)點(diǎn)的顶瞳,不能直接對比,所以要對比誤差愕秫。

        while (end - start >= 1e-6) {
            double mid = start + (end - start) / 2;
            if (checkValid(nums, k, mid)) {
                start = mid;
            } else {
                end = mid;
            }
        }
        return end;

怎樣查存不存在這樣的字?jǐn)?shù)組呢慨菱?用prefix sum。但是不是來存和戴甩,而是和減去中間數(shù)符喝。這樣sums[j] - sums[i]就是從ij的平均值減去中間數(shù)的結(jié)果。舉個(gè)例子甜孤,[1, 2, 3]的sums是[0, 1, 3, 6]协饲,如果中間數(shù)是1畏腕,那么每次計(jì)算下一個(gè)和的時(shí)候,減去中間數(shù)茉稠,sums就變成了[0, 0, 1, 3]描馅。第一個(gè)不需要減,因?yàn)樗韘ize為0的字?jǐn)?shù)組而线,只是用來幫助計(jì)算從第一個(gè)開始字?jǐn)?shù)組的和铭污。這樣,我們就發(fā)現(xiàn)膀篮,如果i是0嘹狞,j是1,那么 sums[j] - sums[i]的結(jié)果就是0誓竿,代表著如果這個(gè)只有1的字?jǐn)?shù)組磅网,平均值不大于我們的中間數(shù)1。而如果j是2筷屡,那么sums[j] - sums[i]的結(jié)果就是1涧偷,大于0,所以這個(gè)包含1毙死,2的字?jǐn)?shù)組燎潮,平均值就大于1。同理规哲,如果中間數(shù)是2跟啤,我們就會發(fā)現(xiàn)sums變成了[0, -1, -1, 0],所以任何兩個(gè)數(shù)相減大于0唉锌,也就代表著最大的平均值不超過2隅肥。

    public boolean checkValid(int[] nums, int k, double mid) {
        double minPrev = 0;
        double[] sums = new double[nums.length + 1];
        sums[0] = 0.0;
        for (int i = 0; i < nums.length; i++) {
            sums[i + 1] = sums[i] + nums[i] - mid;
            if (i + 1 >= k && sums[i + 1] - minPrev > 0) {
                return true;
            }
            if (i + 1 >= k) {
                minPrev = Math.min(minPrev, sums[i + 2 - k]);
            }
        }
        return false;
    }

具體實(shí)現(xiàn)的方法就是,在算sum的時(shí)候袄简,用前一個(gè)sum加當(dāng)前數(shù)字減去中間數(shù)腥放。如果當(dāng)前個(gè)數(shù)超過k個(gè)了,就找一下k個(gè)之前最小的數(shù)绿语,這樣當(dāng)前sum減去之間最小的數(shù)才會是最大的結(jié)果秃症。如果這個(gè)最大的結(jié)果超過0,代表著最大平均值超過了當(dāng)前的中間數(shù)吕粹,那么返回种柑,然后讓二分法繼續(xù)找下一個(gè)平均數(shù)。如果沒有任何一個(gè)組合超過這個(gè)數(shù)匹耕,那么說明我們的中間數(shù)大于最大平均值聚请,在往小的方向二分。
完整的code

public class Solution {
    /*
     * @param nums: an array with positive and negative numbers
     * @param k: an integer
     * @return: the maximum average
     */
    public double maxAverage(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        double start = (double) nums[0];
        double end = (double) nums[0];
        for (int i = 0; i < nums.length; i++) {
            start = Math.min(start, nums[i]);
            end = Math.max(end, nums[i]);
        }

        while (end - start >= 1e-6) {
            double mid = start + (end - start) / 2;
            if (checkValid(nums, k, mid)) {
                start = mid;
            } else {
                end = mid;
            }
        }

        return end;
    }

    public boolean checkValid(int[] nums, int k, double mid) {
        double minPrev = 0;
        double[] sums = new double[nums.length + 1];
        sums[0] = 0.0;
        for (int i = 0; i < nums.length; i++) {
            sums[i + 1] = sums[i] + nums[i] - mid;
            if (i + 1 >= k && sums[i + 1] - minPrev > 0) {
                return true;
            }
            if (i + 1 >= k) {
                minPrev = Math.min(minPrev, sums[i + 2 - k]);
            }
        }
        return false;
    }
}

分析

第一個(gè)解法,用prefix sum驶赏,空間是O(n)炸卑,時(shí)間因?yàn)閠wo pointer遍歷,是O((n-k)2)煤傍,也可以是O(n2)盖文。
第二個(gè)解法,用二分法加prefix sum蚯姆,空間還是O(n)五续,時(shí)間的話,二分需要O(log(max-min))蒋失,然后查找valid需要O(n)返帕,所以總共就是O(nlog(max-min))桐玻。

總的來說篙挽,第一個(gè)解法更簡便,也是面試時(shí)跟容易想到的一個(gè)解法镊靴。第二個(gè)實(shí)現(xiàn)更復(fù)雜一點(diǎn)铣卡,prefix sum減去中間數(shù)這種trick你硬要說面試中剛想到的也不大可信。而且log(max-min)純粹取決于數(shù)值的大小偏竟。如果只有兩個(gè)數(shù)煮落,一個(gè)Integer.MIN_VALUE,一個(gè)Integer.MAX_VALUE踊谋,然后k是2蝉仇,那么肯定第一種解法要比第二種解法快得多。第二種解法適合數(shù)很多殖蚕,range很小轿衔,即n很大,max-min很小的案例睦疫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末害驹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蛤育,更是在濱河造成了極大的恐慌宛官,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓦糕,死亡現(xiàn)場離奇詭異底洗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)咕娄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門亥揖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谭胚,你說我怎么就攤上這事徐块∥床#” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵胡控,是天一觀的道長扳剿。 經(jīng)常有香客問我,道長昼激,這世上最難降的妖魔是什么庇绽? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮橙困,結(jié)果婚禮上瞧掺,老公的妹妹穿的比我還像新娘。我一直安慰自己凡傅,他們只是感情好辟狈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著夏跷,像睡著了一般哼转。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上槽华,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天壹蔓,我揣著相機(jī)與錄音,去河邊找鬼猫态。 笑死佣蓉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亲雪。 我是一名探鬼主播勇凭,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼匆光!你這毒婦竟也來了套像?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤终息,失蹤者是張志新(化名)和其女友劉穎夺巩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體周崭,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柳譬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了续镇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片美澳。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出制跟,到底是詐尸還是另有隱情舅桩,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布雨膨,位于F島的核電站擂涛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏聊记。R本人自食惡果不足惜撒妈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望排监。 院中可真熱鬧狰右,春花似錦、人聲如沸舆床。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽峭弟。三九已至附鸽,卻和暖如春脱拼,著一層夾襖步出監(jiān)牢的瞬間瞒瘸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工熄浓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留情臭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓赌蔑,卻偏偏與公主長得像俯在,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子娃惯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗(yàn)跷乐。 張土汪:刷leetcod...
    土汪閱讀 12,744評論 0 33
  • 題目描述 給定n個(gè)數(shù)的數(shù)組,找到所有長度大于等于k的連續(xù)子數(shù)組中平均值最大的那個(gè)趾浅。返回那個(gè)最大的平均值愕提。 1 <=...
    HITMiner閱讀 4,088評論 0 1
  • 第1章 第一個(gè)C程序第2章 C語言基礎(chǔ)第3章 變量和數(shù)據(jù)類型第4章 順序結(jié)構(gòu)程序設(shè)計(jì)第5章 條件結(jié)構(gòu)程序設(shè)計(jì)第6章...
    小獅子365閱讀 10,651評論 3 71
  • 機(jī)緣巧合下浅侨,我成為了初美小白球的廈門市城市合伙人。剛進(jìn)來证膨,雖然模式確實(shí)吸引我如输,但是說實(shí)話,覺得還是挺虛的,看不到任...
    施麗萍閱讀 354評論 0 3
  • 窗外不见,各種小蟲熱鬧地開著音樂會澳化,為我敲打文字做免費(fèi)的伴奏。 今日稳吮,仍有許多開心的事: 無限相信閱讀的力量肆捕!讓我們一...
    趙誠彬閱讀 248評論 0 3