【演算法拾遺】三種方法求連續子陣列的最大和
轉載請註明出處:http://blog.csdn.net/ns_code/article/details/20942045
這是一道考的爛的不能再爛的題目,但是依然有很多公司樂於將這樣的題目作為筆試或面試題,足見其經典。
問題是這樣的:一個整數陣列中的元素有正有負,在該陣列中找出一個連續子陣列,要求該連續子陣列中各元素的和最大,這個連續子陣列便被稱作最大連續子陣列。比如陣列{2,4,-7,5,2,-1,2,-4,3}的最大連續子陣列為{5,2,-1,2},最大連續子陣列的和為5+2-1+2=8。
下面按照時間複雜度逐步優化的順序依次給出這三種演算法。
暴力求解法
該方法的思想非常簡單,先找出從第1個元素開始的最大子陣列,而後再從第2個元素開始找出從第2個元素開始的最大子陣列,依次類推,比較得出最大的子陣列。實現程式碼如下:
/*
常規方法,時間複雜度O(n*n)
先從第一個元素開始向後累加,
每次累加後與之前的和比較,保留最大值,
再從第二個元素開始向後累加,以此類推。
*/
int MaxSubSum1(int *arr,int len)
{
int i,j;
int MaxSum = 0;
//每次開始累加的起始位置的迴圈
for(i=0;i<len;i++)
{
int CurSum = 0;
//向後累加的迴圈
for(j=i;j<len;j++)
{
CurSum += arr[j];
if(CurSum > MaxSum)
MaxSum = CurSum;
}
}
return MaxSum;
}
很明顯地可以看出,該方法的時間複雜度為O(n*n)。
分治求解法
考慮將陣列從中間分為兩個子陣列,則最大子陣列必然出現在以下三種情況之一:
1、完全位於左邊的陣列中。
2、完全位於右邊的陣列中。
3、跨越中點,包含左右陣列中靠近中點的部分。
遞迴將左右子陣列再分別分成兩個陣列,直到子陣列中只含有一個元素,退出每層遞迴前,返回上面三種情況中的最大值。實現程式碼如下:
/*
求三個數中的最大值
*/
int Max3(int a,int b,int c)
{
int Max = a;
if(b > Max)
Max = b;
if(c > Max)
Max = c;
return Max;
}
/*
次優演算法,採用分治策略
*/
int MaxSubSum2(int *arr,int left,int right)
{
int MaxLeftSum,MaxRightSum; //左右邊的最大和
int MaxLeftBorderSum,MaxRightBorderSum; //含中間邊界的左右部分最大和
int LeftBorderSum,RightBorderSum; //含中間邊界的左右部分當前和
int i,center;
//遞迴到最後的基本情況
if(left == right)
if(arr[left]>0)
return arr[left];
else
return 0;
//求含中間邊界的左右部分的最大值
center = (left + right)/2;
MaxLeftBorderSum = 0;
LeftBorderSum = 0;
for(i=center;i>=left;i--)
{
LeftBorderSum += arr[i];
if(LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
MaxRightBorderSum = 0;
RightBorderSum = 0;
for(i=center+1;i<=right;i++)
{
RightBorderSum += arr[i];
if(RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
//遞迴求左右部分最大值
MaxLeftSum = MaxSubSum2(arr,left,center);
MaxRightSum = MaxSubSum2(arr,center+1,right);
//返回三者中的最大值
return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);
}
/*
將分支策略實現的演算法封裝起來
*/
int MaxSubSum2_1(int *arr,int len)
{
return MaxSubSum2(arr,0,len-1);
}
設該演算法的時間複雜度為T(n),則:
T(n)= 2T(n/2)+ O(n),且T(1)= 1。
逐步遞推得到時間複雜度T(n)= O(nlogn)。
線性時間演算法
該演算法在每次元素累加和小於0時,從下一個元素重新開始累加。實現程式碼如下:
/*
最優方法,時間複雜度O(n)
和最大的子序列的第一個元素肯定是正數
因為元素有正有負,因此子序列的最大和一定大於0
*/
int MaxSubSum3(int *arr,int len)
{
int i;
int MaxSum = 0;
int CurSum = 0;
for(i=0;i<len;i++)
{
CurSum += arr[i];
if(CurSum > MaxSum)
MaxSum = CurSum;
//如果累加和出現小於0的情況,
//則和最大的子序列肯定不可能包含前面的元素,
//這時將累加和置0,從下個元素重新開始累加
if(CurSum < 0)
CurSum = 0;
}
return MaxSum;
}
顯然,該演算法的時間複雜度O(n)。該演算法理解起來應該不難,但是要想出來可就不容易了。另外,該演算法的一個附帶的有點是:它只對資料進行一次掃描,一旦元素被讀入並被處理,它就不再需要被記憶。因此,如果陣列在磁碟或磁帶上,他就可以被順序讀入,在主存中不必儲存陣列的任何部分。不僅如此,在任意時刻,該演算法都能對它已經讀入的資料給出最大子陣列(另外兩種演算法不具有這種特性)。具有這種特性的演算法叫做聯機演算法。
相關文章
- 連續子陣列的最大和陣列
- JZ-030-連續子陣列的最大和陣列
- 每日一練(22):連續子陣列的最大和陣列
- 牛客題霸--連續子陣列的最大和陣列
- 劍指office--31. 連續子陣列的最大和陣列
- 劍指Offer-連續子陣列中的最大和陣列
- 劍指 Offer 42.連續子陣列的最大和陣列
- 劍指offer-例題 連續子陣列的最大和陣列
- 《劍指Offer》- 連續子陣列的最大和或最小和陣列
- [每日一題] 第十五題:連續子陣列的最大和每日一題陣列
- 劍指Offer:JZ30-連續子陣列最大和(解題思路+Java程式碼)陣列Java
- 最短無序連續子陣列陣列
- 最大連續子陣列和的實現陣列
- leetcode最短無序連續子陣列LeetCode陣列
- 【leetcode】53. Maximum Subarray 連續子序列的最大和LeetCode
- 雙指標查詢陣列的連續規律子陣列問題指標陣列
- 【陣列】1550. 存在連續三個奇數的陣列(簡單)陣列
- 最大連續子陣列和(最大子段和)陣列
- 【JS】JS陣列新增元素的三種方法JS陣列
- 程式設計求一維陣列中最大和最小的元素值程式設計陣列
- [C#.NET 拾遺補漏]02:陣列的幾個小知識C#陣列
- C++陣列的連續性C++陣列
- 在PHP中陣列遍歷的三種方法PHP陣列
- 給出一個由[-100,100]之間整陣列成的陣列,求其相加和最大的連續子陣列 輸入 一個連續整陣列成的陣列 輸出 子陣列相加的最大值 樣例輸入 -......陣列
- 最大連續子陣列和求解問題(C語言)陣列C語言
- 【LeetCode動態規劃#14】子序列系列題(最長遞增子序列、最長連續遞增序列、最長重複子陣列、最長公共子序列)LeetCode動態規劃陣列
- 978 最長湍流子陣列陣列
- Leetcode 陣列中和為給定值的最長子陣列LeetCode陣列
- 陣列[簡單]1550. 存在連續三個奇數的陣列2020/11/14(6)陣列
- Google 面試題 | 3個非重複子陣列最大和Go面試題陣列
- 求陣列長度的兩種方法,以及區別(strlen sizeof)陣列
- java_求列舉所有的連續(或單個字元)的子串.Java字元
- Day 45 | 300.最長遞增子序列 、674. 最長連續遞增序列 、718. 最長重複子陣列陣列
- 程式碼隨想錄演算法訓練營 | 300.最長遞增子序列,674. 最長連續遞增序列,718. 最長重複子陣列演算法陣列
- 遍歷陣列的幾種方法陣列
- 陣列去重的幾種方法陣列
- 陣列去重的六種方法陣列
- mongo對文件中陣列進行過濾的三種方法Go陣列
- Zepto核心模組之工具方法拾遺