不可變數(shù)組求和

標(biāo)注:

本文有參考 別人家的面試題:不可變數(shù)組快速范圍求和

來源:十年蹤跡的博客

本文并非抄襲月影的文章,而是加入了自己的認(rèn)識(shí)。

解題思路:

最笨的實(shí)現(xiàn)方式就是遍歷區(qū)間求和。但是題目要求:

sumRange 可能會(huì)被使用很多次,求不同范圍的值

既然多次使用,為避免重復(fù)計(jì)算,應(yīng)該容易想到緩存計(jì)算結(jié)果。

如何緩存

雖然想到了緩存方案,但具體如何緩存呢,緩存的數(shù)據(jù)結(jié)構(gòu)應(yīng)該是什么樣子?怎么應(yīng)用緩存的結(jié)果?

思考過程:

區(qū)間的取值:
i 的范圍 0 - n,j 的范圍 0 - n。 即 0 ~ i ~ j ~ n;

倒推法:我們要的結(jié)果是區(qū)間范圍 [i,j] 之和,按照直觀的想法,我們很容易想到遍歷 [i,j] 然后累加計(jì)算,然后順著這個(gè)思路,如果采用遍歷累加計(jì)算方案,我們能緩存下來什么?緩存每次的計(jì)算結(jié)果? 這樣就是查表法了。

可是仔細(xì)想一想,這個(gè)排列組合有多少種?參考下 i,j 的取值范圍,n*n,也就是至少要計(jì)算 n*n 次才能得到這個(gè)二維數(shù)組。

第一次遇到這個(gè)題目的時(shí)候,我也是按照上面的思考過程,可惜以上的思維太過僵硬。

后參考別人的文章,得到如下轉(zhuǎn)換公式:

[i,j] = [0,j] - [0,i - 1]

區(qū)間 [i, j] 之和 = 區(qū)間 [0, j] 之和 - 區(qū)間 [0, i - 1]之和。

這樣就可以緩存0~n之間的和,排列組合有n種,比二維表性能要好很多,因?yàn)橛?jì)算次數(shù)大大減少了。

解法版本一

const nums = Object.freeze([-2, 0, 3, -5, 2, -1]);

// 緩存數(shù)組
let catchArr = [];

// 初始緩存函數(shù)
const catchFn = function () {
    if(catchArr.length === 0) { // catchArr只緩存一次
        let sum = 0;
        for(let i = 0; i < nums.length; i++) {
            sum += nums[i];
            catchArr.push(sum);
        }
    }
}

let sumRange = (i,j) => {
    catchFn();
    return i >= 1 ? catchArr[j] - catchArr[i - 1] : catchArr[j];
}

可以發(fā)現(xiàn),i - 1 存在邊界判斷,這樣每次計(jì)算都要判斷一次,如果計(jì)算10000次,那性能開銷會(huì)很大。
這里有個(gè)小技巧,catchArr[n] 代表的是 0~n 的和,如果在catchArr頭部增加一個(gè)元素0,那么此時(shí)catchArr[n]代表的就是 0 ~ (n-1) 的值,所以此時(shí)i~j的區(qū)間和就是catch[j+1] - catch[i]。因?yàn)閕最小值是0,所以不會(huì)有邊界問題了。得到版本二:

const nums = Object.freeze([-2, 0, 3, -5, 2, -1]);

// 緩存數(shù)組
let catchArr = [0];
const catchFn = function () {
    if(catchArr.length === 1) { // catchArr只緩存一次
        let sum = 0;
        for(let i = 0; i < nums.length; i++) {
            sum += nums[i];
            catchArr.push(sum);
        }
    }
}

let sumRange = (i,j) => {
    catchFn();
    return catchArr[j + 1] - catchArr[i];
}

以上代碼中,每次調(diào)用sumRange()都會(huì)執(zhí)行catchFn,在里面判斷是否初始化過,所以可以在第一次初始化之后修改sumRange函數(shù)。得到版本三:

const nums = Object.freeze([-2, 0, 3, -5, 2, -1]);

// 緩存數(shù)組
let catchArr = [0];
let sumRange;
const catchFn = function () {
    if(catchArr.length === 1) { // catchArr只緩存一次
        let sum = 0;
        for(let i = 0; i < nums.length; i++) {
            sum += nums[i];
            catchArr.push(sum);
        }
              // 重置sumRange函數(shù)
              sumRange = (i,j) => catchArr[j + 1] - catchArr[i];
    }
}

sumRange = (i,j) => {
    catchFn();
    return catchArr[j + 1] - catchArr[i];
}
小貼士

除了重置sumRange函數(shù)之外,其實(shí)將catchFn重置為空函數(shù),一樣可以避免if判斷,但是js執(zhí)行空函數(shù),會(huì)很耗性能。下圖為本人測(cè)試結(jié)果:

其中需要注意的是:重置sumRange之后,雖然減少了if開銷,但是只在百萬次以內(nèi)執(zhí)行時(shí)間減少了,而在百萬次之后,反而時(shí)間變大,尚切不知道是為什么。

性能測(cè)試.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在C語言中,五種基本數(shù)據(jù)類型存儲(chǔ)空間長(zhǎng)度的排列順序是: A)char B)char=int<=float C)ch...
    夏天再來閱讀 4,035評(píng)論 0 2
  • 各校歷年復(fù)試機(jī)試試題 清華、北大、華科試題詳細(xì)筆記部分,少筆記部分與少數(shù)leetcode【含個(gè)人整理筆記】 一、詳...
    AIM外星人閱讀 1,328評(píng)論 0 1
  • 靜靜的, 微微一笑, 無法移開我的視線, 像自然的恩賜, 是秋天飽滿的果實(shí)。 無言傳遞,心中幸福溢出。 不,此刻請(qǐng)...
    閑時(shí)翻書君閱讀 228評(píng)論 0 2
  • 身處復(fù)雜死結(jié)中, 抱怨反抗都沒用。 聰明展現(xiàn)更危...
    魚翔淺底韜光養(yǎng)晦閱讀 135評(píng)論 0 1
  • ?01 最近看了一部電影《前任攻略》里面有一句臺(tái)詞印象特別深刻。張涵予大叔說:他們那代人,東西壞了會(huì)修,而我們這代...
    進(jìn)擊的歷史君閱讀 402評(píng)論 0 0

友情鏈接更多精彩內(nèi)容