1. 程式人生 > >程式設計之美4:那些常被考到的關於陣列的最大子陣列問題

程式設計之美4:那些常被考到的關於陣列的最大子陣列問題

樓主這篇文章的目的是要帶大家梳理一下,有關於求子陣列問題。如求子陣列的最大和,求最大和的子陣列,求最大積的子陣列等一系列問題。今天陽光明媚,樓主今天心情很好哦,願大家開心每一天,哈哈。Are you ready?開始了哦~~~

題目一:求子陣列的最大和

題目求子陣列的最大和,這裡需要注意的一個問題就是,子陣列那麼便意味著是連續的一段資料。我們可以先寫的例子,方便我們注意到要考慮的一些問題。

陣列:[1, -2, 3, 5, -3, 2] 應該返回8,最大和的子陣列為3,5
陣列:[0, -2, 3, 5, -1, 2] 應該返回9,最大和的子陣列為3, 5,-1,2
陣列:[-1, -2, -3, -4, -5]應該返回-1,最大和的子陣列為-1
這裡,我們只介紹一種時間複雜度為O

(N)的演算法來求解。

解法一:分治法

將所給陣列(A[0],A[1],...,A[N1])分成兩組,(A[0],A[1],...,A[N21])(A[N2],A[N2+1],...,A[N1]),分別求出這兩段陣列各自的最大子段和,則原陣列的子陣列的最大和可以分為下面三種情況:

  • (A[0],A[1],...,A[N21])相同
  • (A[N2],A[N2+1],...,A[N1])相同
  • 跨過中間兩個元素A[N21])A[N2])

看下面程式碼:

#include <iostream>  
#include <limits>

using namespace
std; int getMAX3(int a, int b, int c); int getBiggestSubSum(int *pArray, int low, int high); int main() { int a1[] = {1, -2, 3, 5, -3, 2}; int a2[] = {1, -2, 3, 5, -1, 2}; int a3[] = {-1, -2, -3, -5, -3, -2}; cout << "陣列1的子數子的最大和為" << getBiggestSubSum(a1, 0, sizeof(a1) / sizeof(int
) - 1) << endl; cout << "陣列2的子數子的最大和為" << getBiggestSubSum(a2, 0, sizeof(a2) / sizeof(int) - 1) << endl; cout << "陣列3的子數子的最大和為" << getBiggestSubSum(a3, 0, sizeof(a3) / sizeof(int) - 1) << endl; system("pause"); } int getBiggestSubSum(int *pArray, int low, int high) { int middle = (low + high) >> 1; //獲取中間節點的下標 int MAX = numeric_limits<int>::min(); if (high == low) { //遞迴到只剩下一個數之後,那麼直接返回 return pArray[low]; } int lMax = numeric_limits<int>::min(); int rMax = numeric_limits<int>::min(); int mMax = numeric_limits<int>::min(); int mMaxTemp = 0; //求左半部分的最大和 lMax = getBiggestSubSum(pArray, low, middle); //求右半部分的最大和 rMax = getBiggestSubSum(pArray, middle + 1, high); //求橫跨中間的最大和 for (int i = middle; i <= high; i++) { mMaxTemp += pArray[i]; mMax = max(mMax, mMaxTemp); } for (int i = middle - 1; i >= 0; i--) { mMaxTemp += pArray[i]; mMax = max(mMax, mMaxTemp); } //返回三大部分最大和之間的最大值 return getMAX3(rMax, mMax, lMax); } int getMAX3(int a, int b, int c) { int max = a; max = (b > max) ? (b):(max); max = (c > max) ? (c):(max); return max; }

演算法複雜度:O(NlogN)

解法二:動態規劃法

我們可以考慮陣列的第一個元素A[0],以及最大的一段陣列(A[i],...,A[j])跟$A[0]的關係,有以下幾種情況:

  • i = 0 = j,這便意味著,最大和的子陣列只由一個A[0]組成;
  • 0 = i < j,這便意味著,最大和的子陣列是從A[0]開始的一串序列組成;
  • 0 < i < j,這便意味著,最大和的子陣列和A[0]無關,不是從A[0]開始的。

從上面三種情況可以看出來,我們可以將一個大問題,轉化成一個較小的問題。如本題中,我們可以將一個N維陣列的子陣列最大和問題,轉化成N-1維的子陣列最大和問題。假設已經知道(A[1],...,A[N1])子陣列的最大和為All[1],還知道包含A[1]的子陣列的最大和為Start[1]。那麼根據上面的分析可知

All[0]=max{A[0],A[0]+Start[0],All[1]}

上述公式中,第一項表示只有一個A[0]元素構成最大和子陣列,第二項表示A[0]和從A[1]開始的序列構成最大和子陣列,第三項表示,最大和子陣列和A[0]無關。看下面程式碼:

#include <iostream>  

using namespace std;  

int getBiggestSubSum(int *pArray, int len);

int main()  
{  
    int a1[] = {1, -2, 3, 5, -3, 2};
    int a2[] = {1, -2, 3, 5, -1, 2};
    int a3[] = {-1, -2, -3, -5, -3, -2};

    cout << "陣列1的子數子的最大和為" << getBiggestSubSum(a1, sizeof(a1) / sizeof(int)) << endl;
    cout << "陣列2的子數子的最大和為" << getBiggestSubSum(a2, sizeof(a2) / sizeof(int)) << endl;
    cout << "陣列3的子數子的最大和為" << getBiggestSubSum(a3, sizeof(a3) / sizeof(int)) << endl;
    system("pause");
}  

int getBiggestSubSum(int *pArray, int len)
{
    //從陣列的最後一個元素開始,當只有一個元素的時候,那麼start和all當然都是等於這個數啦
    int nStart = pArray[len - 1];
    int nAll = pArray[len - 1];

    for (int i = len - 2; i >= 0; i--)
    {
        nStart = max(pArray[i], nStart + pArray[i]);
        nAll = max(nAll, nStart);
    }
    return nAll;
}

執行結果:

陣列1的子數子的最大和為8
陣列2的子數子的最大和為9
陣列3的子數子的最大和為-1
請按任意鍵繼續. . .

演算法複雜度:O(NlogN)

題目二:求子陣列的最大和並輸出相應的子陣列

題目一隻管求出子陣列的最大和,不管子陣列是什麼。而這個題目中,我們需要求解子陣列序列的起始位置和結束位置。其實和上面差不多,只是需要記錄位置而已,看程式碼,在題目一的解法二上面修改的。

#include <iostream>  

using namespace std;  

int getBiggestSubSum(int *pArray, int len, int *startIndex, int *endIndex);
void printMaxSubArray(int *pArray, int start, int end);

int main()  
{  
    int a1[] = {1, -2, 3, 5, -3, 2};
    int a2[] = {1, -2, 3, 5, -1, 2};
    int a3[] = {-1, -2, -3, -5, -3, -2};

    int start = 0;
    int end = 0;

    cout << "陣列1的子數子的最大和為" << getBiggestSubSum(a1, sizeof(a1) / sizeof(int), &start, &end) << endl;
    cout << "和最大的子序列為" << endl;
    printMaxSubArray(a1, start, end);

    cout << "陣列2的子數子的最大和為" << getBiggestSubSum(a2, sizeof(a2) / sizeof(int), &start, &end) << endl;
    cout << "和最大的子序列為" << endl;
    printMaxSubArray(a2, start, end);
    cout << "陣列3的子數子的最大和為" << getBiggestSubSum(a3, sizeof(a3) / sizeof(int), &start, &end) << endl;
    cout << "和最大的子序列為" << endl;
    printMaxSubArray(a3, start, end);
    system("pause");
} 


int getBiggestSubSum(int *pArray, int len, int *startIndex, int *endIndex)
{
    //從陣列的最後一個元素開始,當只有一個元素的時候,那麼start和all當然都是等於這個數啦
    int nStart = pArray[len - 1];
    int nAll = pArray[len - 1];
    *startIndex = len - 1;
    *endIndex = len - 1;

    for (int i = len - 2; i >= 0; i--)
    {
        if (pArray[i] > nStart + pArray[i]) 
        {       
            nStart = pArray[i];
            if (nStart > nAll)
            {
                //如果僅僅包含pArray[i]的子序列獲得最大和的話,那麼startIndex和endIndex和nAll都需要更新
                *startIndex = i;
                *endIndex = i;
                nAll = pArray[i];
            }
            else
            {
                //還是原來的子序列和最大,那startIndex和endIndex都不變了
            }
        }
        else
        {
            nStart = nStart + pArray[i];
            if (nStart > nAll)
            {
                //如果包含a[i],a[i+1],...a[j]獲得最大的和的話,那麼要更新startIndex和nAll
                nAll = nStart;
                *startIndex = i;
            }
            else
            {
                //還是原來的子序列和最大,那startIndex和endIndex都不變了
            }
        }
    }
    return nAll;
}

void printMaxSubArray(int *pArray, int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        cout << pArray[i] << " ";
    }
    cout << endl;
}

時間複雜度同樣是O(N)

題目三:求子陣列的最大積

這裡必須要重述一下題目,和題目一以及題目二是不一樣的!!!!

給定一個長度為N的整數陣列,只允許用乘法,不能用除法,計算任意N-1個數的組合乘積中的最大的一組,並寫出演算法的時間複雜度。

這裡的子陣列,是任意N-1個數的組合,並不一定是連續的N-1的子序列。

解法一

其實這道題,騰訊某年筆試題的加試題就有這道題,感興趣的同學可以看這個連結2012年騰訊實習生筆試附加題
題目簡介:已知陣列a[n],求陣列b[n].要求:b[i]=a[0]a[1]……*a[n-1]/a[i],不能用除法。a.時間複雜度O(n),空間複雜度O(1)。 b.除了迭代器i,不允許使用任何其它變數(包括棧臨時變數等),看,是不是差不多?

這裡我們同樣可以使用前累乘後累乘的辦法把所有的b[i]求出來,然後遍歷一遍,求出最大的積。看程式碼:

#include <iostream>  

using namespace std;  

void getBiggestMultipleResults(int *pArray, int *pResultsArray, int len);

int main()  
{  
    int a[] = {1, 2, 3, 4};
    int len = sizeof(a) / sizeof(int);
    int *b = new int[len]();
    int result = b[0];

    //求出所有的b[i]
    getBiggestMultipleResults(a, b, len);

    for (int i = 0; i < len; i++)
    {
        result = max(b[i], result);
    }

    cout << result << endl;

    delete []b;
    system("pause");
} 


void getBiggestMultipleResults(int *pArray, int *pResultsArray, int len)
{
    pResultsArray[0] = 1;
    //前累乘
    for (int i = 1; i < len; i++)
    {
        pResultsArray[i] = pResultsArray[i - 1] * pArray[i - 1];
    }

    //後累乘
    for (int i = len - 2; i > 0; i--)
    {
        //把pResultsArray[0]作為臨時變數
        pResultsArray[0] *= pArray[i + 1];
        pResultsArray[i] *= pResultsArray[0];
    }

    pResultsArray[0] *= pArray[1];
}

解法二:數學分析法

看完這個數學分析的方法,不禁感嘆,還是要數學好啊!不信,你看吧。

假設N個證書的乘積為P, 針對P的正負性進行分析:

1. P=0

那麼,陣列中至少有1個0。除去這個0之外,其他N-1個數的乘積為Q,根據Q的正負性進行分析:

  • Q = 0

結果返回0.
分析:Q=0說明Q數組裡面還有1個0,加上前面P陣列中的1個0,我們可以斷定,任意N-1個數的乘積只能為0,那麼結果返回0.

  • Q > 0

結果返回Q
分析:如果以0替換此時Q數組裡面任意一個數的話,那麼結果都是0,所以最後結果為Q

  • Q < 0

結果返回0
分析:如果以0替換此時Q數組裡面的任意一個,則結果為0,0比負數大,所以結果返回0

2. P > 0

從陣列中去除一個絕對值最小的正整數,這樣得到剩下的

相關推薦

程式設計4那些到的關於陣列大子陣列問題

樓主這篇文章的目的是要帶大家梳理一下,有關於求子陣列問題。如求子陣列的最大和,求最大和的子陣列,求最大積的子陣列等一系列問題。今天陽光明媚,樓主今天心情很好哦,願大家開心每一天,哈哈。Are you ready?開始了哦~~~ 題目一:求子陣列的最大和

程式設計4階乘相關

1)N!末尾有多少個零 N! = K * 10^M N! = 2^X * 3^Y * 5^Z M = min(X,Z) 其中X >= Z,因為能被2整除的數出現的頻率比能被5整除的數高很多。 於是只需計算因式分解中5的指數。 1: ret = 0; for (int i = 1; i <

程式設計1那些關於1的個數的經典面試題

那些關於1的個數的經典面試題 好長時間沒有練演算法了,筆試題一做,發現非常吃力,所以近日來找來《程式設計之美》一書來看看練練。為了激勵自己多練,樓樓可能會出個專欄什麼的,感興趣的同學我們可以一起抱團,樓樓也會保證每天都會更新。那今天呢,就是《程式設計之美》的第

程式設計7字串,那些你必須要會的事。

本系列收錄了常見字串面試和筆試中的八道題,更新於2015年4月23日。 如果有問題或想法,請直接留言,交流。 題目一:字串移位包含 問題描述: 給定兩個字串s1和s2,要求判定s2是否能夠被通過迴圈移位得到的字串包含。例如,給定s1 = AABCD和

程式設計10計算字串的相似度

我們並不在乎兩個字串變得相等之後的字串是怎樣的,所以 1.一步操作之後,再將A[2,…,lenA]和B[1,…,lenB]變成相同的字串。 2.一步操作之後,再將A[1,…,lenA]和B[2,…,lenB]變成相同的字串。 3.一步操作之後,再將A[2,…,lenA]和B[2,…,lenB]變成相

程式設計9陣列迴圈位移

1: RightShift(int *arr, int N, int K) { K %= N; while (K--) { int t = arr[N - 1]; for (int i = N - 1; i > 0; i--)

程式設計8陣列的子陣列之和的大值

1: int MaxSum(int *A, int n) { int maximum = -INF; int sum; for (int i = 0; i < n; i++) { sum = 0; for (int j = i;

程式設計7大公約數

1:輾轉相除法 f(x,y) = f(y, x%y); int gcd(int x, int y) { return (!y) ? x : gcd(y, x % y); } 2:對於大整數,取模運算非常昂貴。 f(x, y) = f(x-y, y); BigInt gcd(BigI

程式設計3求二進位制數中1的個數

1: int Count(BYTE v) { int num = 0; while (v) { if (v % 2 == 1) { num++; } v = v / 2; }

程式設計2程式只用一個位元組變數,列印將帥位置

原創:https://blog.csdn.net/ndzjx/article/details/84404320 #include <stdio.h> #include <windows.h> #include <time.h> #include <

程式設計1CPU列印直線,曲線

原創:https://blog.csdn.net/ndzjx/article/details/84404268 1:本質:每次迴圈的CPU比例問題。 CPU排程時間片,大約為20ms 2.2GHz是CPU時鐘週期,= 22億次 = 2.2*10^9 每個時鐘週期平均執行2條彙編指令

程式設計8連結串列常見面試筆試題集合

樓樓這篇文章決定把面試中關於連結串列的常見面試題或者筆試題整理一下,現在目前為止只整理了四個題目,後面如果樓主看到還有什麼題目需要記錄的話,會一直更新的。樓樓略菜,如果有什麼錯誤或不對的地方,希望各位看官留言指出,謝謝啦! 今天又是福來day了,好傷心啊,一週

程式設計4.3 買票找零

題目描述: 假設有2N個人在排隊買票,其中有N個人手持50元的鈔票,另外有N個人手持100元的鈔票,假設開始售票時,售票處沒有零錢,問這2N個人有多少種排隊方式,不至使售票處出現找不開錢的局面? 題目分析: 這題時典型的卡特蘭數(Cartalan)問題 Ca

程式設計6陣列迴圈移位

樓主又來~(≧▽≦)/~啦啦啦,科研,就是要這麼一鼓作氣。額,其實樓主的老本行是推公式啊,做這些演算法題,其實是樓主在偷懶。額,話不多說了,快請出我們今天的主角吧!還是關於陣列的-陣列迴圈移位。 下面我們來看下題目的要求。 題目要求: 設計一個演算法,

程式設計2尋找大的K個數

根據樓樓參加筆試或者面試的經驗而言,尋找最大的K個數這個問題,被問到已經不只兩三次了,所以樓樓決定認認真真地把這個問題寫一下,解法思想參照《程式設計之美》一書。 題目簡介 有很多無序的數,我們姑且假定他們各不相等,怎麼選出其中最大的K個數呢? 相關知識點

程式設計4.3買票找零

解法二的筆記: 存在某個k,使序列前k項中1的個數比0的個數剛好小1? 假設不存在這樣的k,那麼由於是非法序列那麼k=2n-1時,1的個數至少比0小於2,這樣無論第2n個數取0還是取1,0和1的總個數均不會相等,所以假設不成立。 必存在k使序列前k項中1的個數比0的個數剛

程式設計-1.4-買書問題

問題描述: 《哈利波特》1-5卷促銷活動,每本8元。買不同的n本可以對應不同的折扣如下。 求解一筆訂單中,購買不同卷數不同本數的最少價格解。 問題思考: 書中給出了兩種解題思路: 思路一: 參照上一小節,仍使用遞迴方式遍歷所有解,取得最優解。 思路二: 採用貪

程式設計求二進位制中1的個數

1.問題描述 實現一個函式,輸入一個無符號整數,輸出該數二進位制中的1的個數。例如把9表示成二進位制是1001,有2位是1,因此如果輸入9,該函式輸出2 2.分析與解法 解法1:利用十進位制和二進位制相互轉化的規則,依次除餘操作的結果是否為1  程式碼如下: int Count1(unsigned

Java 併發程式設計併發程式設計高階篇之一-chat

借用 Java 併發程式設計實踐中的話:編寫正確的程式並不容易,而編寫正常的併發程式就更難了。相比於順序執行的情況,多執行緒的執行緒安全問題是微妙而且出乎意料的,因為在沒有進行適當同步的情況下多執行緒中各個操作的順序是不可預期的。 併發程式設計相比 Java

Python書籍推薦《Python程式設計最佳實踐指南》

        《Python程式設計之美:最佳實踐指南》的作者之一就是大名鼎鼎的K神,Kenneth Reitz,也就是requests庫的作者。在這裡必須誇一下requests庫,真是太好用了,能用requests絕不用urllib、urllib2。K神出品,必屬精品!!