日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

真香!8 行代码搞定最大子数组和问题

發布時間:2024/8/23 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 真香!8 行代码搞定最大子数组和问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 | 碼農的荒島求生

來源 |?碼農的荒島求生

今天給大家帶來一道極其經典的題目,叫做最大和子數組,給定一個數組,找到其中的一個連續子數組,其和最大。

示例:

輸入:?nums?=?[-2,1,-3,4,-1,2,1,-5,4]?

輸出:?6?

解釋:?子數組[4,-1,2,1]的和為6,其它任何連續的子數組和不超過6。

想一想該怎樣解決這個問題。

如果你一時想不到解法可以從暴利解法開始。

暴力求解

這種解法最簡單,我們把所有子數組找出來,然后依次計算其和,找出一個最大的出來,比如給定數組[1,2,3],那么我們能找出子數組:[1],[2],[3],[1,2],[2,3],[1,2,3],很顯然這里和最大的子數組為[1,2,3],其值為6。

int sum(vector<int>&nums, int b,int e){int res = 0;for (; b <= e; b++) {res += nums[b];}return res; } int maxSubArray(vector<int>& nums) {int size = nums.size();int res = 0x80000000;for (int i = 0; i < size; i++) {for (int j = i; j < size; j++) {res = max(res, sum(nums, i, j));}}return res; }

這種解法最簡單,該算法的時間復雜度為O(n^3),其中找出所有子數組的時間復雜度為O(n^2),計算每個子數組的和的時間復雜度為O(n),因此其時間復雜度為O(n^3)。

讓我們再來看一下這個過程,這里的問題在于計算每個子數組的和時有很多重復計算,比如我們知道了子數組[1,2]的和后再計算數組[1,2,3]的值時完全可以利用子數組[1,2]的計算結果而無需從頭到尾再算一遍,也就是說我們可以利用上一步的計算結果,這本身就是動態規劃的思想

基于該思想我們可以對上述代碼簡單改造一下:

int maxSubArray(vector<int>& nums) {int size = nums.size();int res = 0x80000000;for (int i = 0; i < size; i++) {int sumer = nums[i];res = max(res, sumer);for (int j = i + 1; j < size; j++) {sumer += nums[j];res = max(res, sumer);}}return res; }

看到了吧,代碼不但更簡潔,而且運行速度更快,該算法的時間復雜度為O(n^2),比第一種解法高了很多。

還有沒有進一步提高的空間呢?

答案是肯定的。

分而治之

我們可以把整個數組一分為二,然后子數組也一分為二,不斷劃分下去就像這樣:

然后呢?

然后問題才真正開始有趣起來,注意,當我們劃分到最下層的時候,也就是不可再劃分時會得到兩個數組元素,比如對于數組[1,2]會劃分出[1]與[2],此時[1]與[2]不可再劃分,那么對于子問題[1,2],其最大子數組的和為max(1+2, 1,2),也就是說要么是左半部分的元素值、要么是右半部分的元素值、要么是兩個元素的和,就這樣我們得到了最后兩層的答案:

假設對于數組[1,2,3,4],一次劃分后得到了[1,2]與[3,4],用上面的方法我們可以分別知道這兩個問題的最大子數組和,我們怎樣利用上述的答案來解決更大的問題,也就是[1,2,3,4]呢?

很顯然,對于[1,2,3,4]來說,最大子數組的和要么來自左半部分、要么來自右半部分、要么來自中間部分——也就是包含2和3,其中左半部分和右半部分的答案我們有了,那么中間部分的最大和該是多少呢?

其實這個問題很簡單,我們從中間開始往兩邊不斷累加,然后記下這個過程的最大值,比如對于[1,-2,3,-4,5],我們從中間的3開始先往左邊累加和是:{1+(-2)+3, (-2)+3, 3}也就是{2,1,3},因此我們以中間數字為結尾的最大子數組和為3:

另一邊也是同樣的道理,只不過這次是以中間數字為起點向右累加:

然后這三種情況中取一個最大值即可,這樣我們就基于子問題解決了更大的問題:

此后的道理一樣,最終我們得到了整個問題的解。

根據上面的分析就可以寫代碼了:

int getMaxSum(vector<int>& nums, int b, int e) {if (b == e) return nums[b];if (b == e - 1) return max(nums[b], max(nums[e], nums[b]+nums[e]));int m = (b + e) / 2;int maxleft = nums[m];int maxright = nums[m];int sum = nums[m];for (int i = m + 1; i <= e; i++) {sum += nums[i];maxright = max(maxright, sum);}sum = nums[m];for (int i = m - 1; i >= b; i--) {sum += nums[i];maxleft = max(maxleft, sum);}return max(getMaxSum(nums, b, m - 1), max(getMaxSum(nums, m + 1, e), maxleft+maxright-nums[m])); } int maxSubArray(vector<int>& nums) {return getMaxSum(nums, 0, nums.size()-1); }

上述這段代碼的時間復雜度為O(NlogN)比第二種方法又提高了很多。

動態規劃

實際上這個問題還有另一種更妙的解決方法,我們令dp(i)表示以元素A[i]為結尾的最大子數組的和,那么根據這一定義則有:

這是很顯然的,注意dp(i)的定義,是以元素A[i]為結尾的最大子數組的和,因此dp(i)的值要么就是A[i]連接上之前的一個子數組,那么不鏈接任何數組,那么最終的結果一定是以某個元素為結尾的子數組,因此我們從所有的dp(i)中取一個最大的就好了,依賴子問題解決當前問題的解就是所謂的動態規劃。

有了這些分析,代碼非常簡單:

int maxSubArray(vector<int>& nums) {int size = nums.size();vector<int> dp(size, 0);int res = dp[0] = nums[0];for (int i = 1; i < size; i++) {dp[i] = max(dp[i - 1] + nums[i], nums[i]);res = max(res, dp[i]);}return res; }

這段代碼簡單到讓人難以置信,只有8行代碼,你甚至可能會懷疑這段代碼的正確性,但它的確是沒有任何問題的,而且這段代碼的時間復雜度只有O(N),這段代碼既簡單運行速度又快,這大概就是算法的魅力吧。

往期推薦

從 40% 跌至 4%,“糊”了的 Firefox 還能重回巔峰嗎?

Gartner 發布 2022 年汽車行業五大技術趨勢

別再用 Redis List 實現消息隊列了,Stream 專為隊列而生

漫畫:什么是“低代碼”開發平臺?

點分享

點收藏

點點贊

點在看

總結

以上是生活随笔為你收集整理的真香!8 行代码搞定最大子数组和问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。