算法:獲取數(shù)組的一個(gè)分界點(diǎn),使得左右兩邊和相等

要求給定一個(gè)數(shù)組,獲取數(shù)組的一個(gè)分界點(diǎn)的下標(biāo),使得該分界點(diǎn)的兩側(cè)子數(shù)組的和相等。如果存在多個(gè)分界點(diǎn),只返回第一個(gè)分界點(diǎn)的下標(biāo),如果沒(méi)有則返回-1。
要求:時(shí)間復(fù)雜度不得超過(guò)O(n)。

測(cè)試用例:

輸入:[-20, 30, 10, 40, 20]
返回:3

分析:正常的計(jì)算思路應(yīng)該是,用一個(gè)index標(biāo)記當(dāng)前計(jì)算的下標(biāo),從數(shù)組第二個(gè)元素(即index為1開(kāi)始)開(kāi)始計(jì)算,將這個(gè)元素左邊的相加求和,然后右邊相加求和,對(duì)比是否相等。然后index++依次遍歷,知道index 遍歷到數(shù)組倒數(shù)第二個(gè)元素,都沒(méi)有找到則返回-1。

Java實(shí)現(xiàn)

按照上面的思路,最直接的算法為:

    static int middleIndexWithArray(Integer[] array) {
        if (array.length < 3) return -1;
        for (int i = 1; i < array.length - 1; i++) {
            int leftSum = 0;
            int rightSum = 0;
            for (int j = 0; j < i; j++) {
                leftSum += array[j];
            }
            for (int k = i + 1; k < array.length; k++) {
                rightSum += array[k];
            }
            if (leftSum == rightSum) {
                return i;
            }
        }
        return - 1;
    }

復(fù)雜度分析:
首先最外層參數(shù)為 i 的for循環(huán)遍歷取決于數(shù)組數(shù)據(jù)大小,數(shù)組長(zhǎng)度為n則最大需要 n - 2 次運(yùn)算。
外層for循環(huán)內(nèi)部的兩個(gè)for循環(huán)的 j + k 最大循環(huán)次數(shù)實(shí)際等于 n - 1。
所以最終:(n - 2)(n - 1) 復(fù)雜度為O(n^2),不符合算法要求。

那么我們需要優(yōu)化的就是如何降低復(fù)雜度,先思考,為什么會(huì)出現(xiàn)這么高的時(shí)間復(fù)雜度。大多數(shù)做計(jì)算對(duì)比的算法復(fù)雜度過(guò)高都是因?yàn)樽隽酥貜?fù)性的運(yùn)算,我們看一下之前的算法并模擬一下計(jì)算的過(guò)程
先模擬一下leftSum的計(jì)算:
當(dāng)i == 1 時(shí),leftSum += array[0]
當(dāng)i == 2時(shí),leftSum += array[0]、leftSum += array[1]
當(dāng)i == 3時(shí),leftSum += array[0]、leftSum += array[1]、leftSum += array[2]
在看一下rightSum的計(jì)算:
當(dāng)i == 1 時(shí),rightSum += array[2]、rightSum += array[3]、rightSum += array[4]
當(dāng)i == 2時(shí),rightSum += array[3]、rightSum += array[4]
當(dāng)i == 3時(shí),rightSum += array[4]
是不是發(fā)現(xiàn)問(wèn)題了,實(shí)際上最外層的for循環(huán)內(nèi)部計(jì)算 leftSum和rightSum進(jìn)行了大量的重復(fù)計(jì)算,當(dāng)i == 3時(shí),leftSum還是將數(shù)組的第0、1、2元素累加,但是之前i==2時(shí),數(shù)組的第0、1元素已經(jīng)做過(guò)一次加法了,那么第一步加法就是多余的,rightSum的計(jì)算同理。

既然發(fā)現(xiàn)了問(wèn)題所在,就是怎么優(yōu)化掉重復(fù)的加法問(wèn)題了?,F(xiàn)在換一種思維方式:
開(kāi)始需要比較的是array數(shù)組下標(biāo)為1的元素,這時(shí)如果知道了整個(gè)數(shù)組的和為allSum。那么左邊子數(shù)組的和leftSum就是array的第一個(gè)元素array[0]。右邊的子數(shù)組的和就是rightSum = allSum - leftSum - array[1];
當(dāng)下一次index2時(shí),將上一次計(jì)算的leftSum用于下一次比較,leftSum只需要再加上array[1],就是左邊數(shù)組的和,而右邊的子數(shù)組的和還可以通過(guò)rightSum = allSum - leftSum - array[1]計(jì)算。
這樣的話,左側(cè)數(shù)組和只需要每次累加一個(gè)新的需要對(duì)比的元素,右側(cè)直接就可以通過(guò)減法計(jì)算不需要累加:

    static int middleIndexWithArray(Integer[] array) {
        if (array.length < 3) return -1;
        int allSum = 0;
        int leftSum = 0;
        int rightSum = 0;
        for (int i = 0; i < array.length; i++) {
            allSum += array[i];
        }
        for (int i = 1; i < array.length - 1; i++) {
            leftSum += array[i - 1].intValue();
            rightSum = allSum - leftSum - array[i].intValue();
            if (leftSum == rightSum) return i;
        }
        return -1;
    }

復(fù)雜度分析:第一個(gè)for循環(huán)計(jì)算allSum取決于數(shù)組容量n,后面for循環(huán)計(jì)算最多需要計(jì)算 n - 2 次,最多需要 2n - 2,時(shí)間復(fù)雜度為 O(n)。

使用包含20萬(wàn)條長(zhǎng)度數(shù)據(jù)的數(shù)組,測(cè)試優(yōu)化后前后時(shí)間對(duì)比:

        TimeTool.check("Midde", new Task() {
            public void execute() {
                Integer[] array = new Integer[200001];
                for (int i = 0; i < 200001; i++) {
                    if (i < 100000) {
                        array[i] = i;
                    } else if (i == 100000) {
                        array[i] = -1;
                    } else {
                        array[i] = 200000 - i;
                    }
                }
                System.out.println(middleIndexWithArray(array));
            }
        });

優(yōu)化前

【Midde】
開(kāi)始:16:08:22.262
100000
結(jié)束:16:08:34.178
耗時(shí):11.916秒
-------------------------------------

優(yōu)化后

【Midde】
開(kāi)始:16:10:12.414
100000
結(jié)束:16:10:12.425
耗時(shí):0.01秒
-------------------------------------

Objective-C實(shí)現(xiàn)

NSInteger middleIndexWithArray(NSArray<NSNumber *> * array) {
    if (array.count < 3) return -1;
    NSInteger leftSum = 0;
    NSInteger rightSum = 0;
    NSInteger allSum = 0;
    // n
    for (NSNumber *sum in array) allSum += sum.integerValue;
    // max: n - 2
    for (NSInteger i = 1; i < array.count - 1; i++) {
        leftSum += array[i - 1].integerValue;
        rightSum = allSum - leftSum - array[i].integerValue;
        if (leftSum == rightSum) return i;
    }
    // 復(fù)雜度 2n - 2 => O(n)
    return -1;
}

總結(jié)

這個(gè)算法題主要就是考察思維邏輯的,并沒(méi)有對(duì)數(shù)據(jù)結(jié)構(gòu)的知識(shí)進(jìn)行深入考察,即便不清楚數(shù)組查找的復(fù)雜度為O(1),也不會(huì)出現(xiàn)錯(cuò)誤,只需要靈活思考就能夠解決,屬于簡(jiǎn)單的問(wèn)題。

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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