這是昨天面試時碰到的一道算法題:任意數分三組,使得每組的和盡量相等(感謝博友提供的關於該問題的相關資料 划分問題)。由於時間倉促,加之面試時頭昏腦漲,這道題沒做出來甚至沒有給出思路,這讓我多少有些遺憾和不甘。因為最近接觸算法的東西較多而且本身對算法感興趣,所以回家之后絞盡腦汁想把這題做出來。其實剛看到這題時感覺不難,但是因為數字個數及數值的不確定,我感覺這題越想越難。昨天一晚上沒有睡好,甚至做夢都在想這題!
今天上午在多個群里問了這題,都沒有給出思路,真是絕望至極。很多人都說 n/3 的思路,其實這種思路一開始就是死胡同。本人屬於那種不會輕言放棄之人,哈哈。本人多年經驗得出一結論:只要認真思考,有耐心,很多問題都能迎刃而解。每次遇到難纏的問題,我都會抓耳撓腮,而一旦解決,又會手舞足蹈。跑遠了,言歸正傳,中午正准備趴在工位上休息,眼睛掃了掃筆記本,瞬間茅塞頓開。真是踏破鐵鞋無覓處,得來全不費工夫!果不其然,算法就是窗戶紙!
我先說一下我的思路,首先一定要先排序,這也是解決問題的關鍵。然后降序排序后的前三個數各分一組(根據博友的評論,此處是問題所在,前三個數也有可能屬於同一組,暫時沒有想到好方法),把剩余數往三個數上疊加。我最開始的思路也是如此,問題在於分組個數不確定,出現極端大的數怎么辦,怎么疊加?那層窗戶紙就是將剩余數中的最大值加到前三個數的最小值上,然后重排序,繼續疊加,直到數組個數剩三個為止!不知道這樣說好不好理解,比如以下數組:
[3, 8, 20, 15, 60, 1, 32]; // 分組流程分解,根據博友評論,此方法屬於貪心算法,只是局部求解: // 1.從大到小排序 [60, 32, 20, 15, 8, 3, 1] // 2.剩余數疊加 [60, 32, 35(20+15), 8, 3, 1] // 3.重排序 [60, 35, 32, 8, 3, 1] // 4.再疊加 [60, 35, 40(32+8), 3, 1] // 5.重排序 [60, 40, 35, 3, 1] // 6.再疊加 [60, 40, 38(35+3), 1] // 7.結果 [60, 40, 39]
我想看完上述流程演示,這個題基本就沒問題了吧。上述流程最終輸出的是每組的和,並沒有反映出每組的分組情況。我們應該定義一個函數,輸出帶有分組情況的數組。比如:
var arr = [3, 8, 20, 15, 60, 1, 32]; function equal(arr) { ...... return array; } > input equal(arr) < output [[60],[32,8],[20,15,8,3,1]]
算法這東西,只有自己親自思考出來才能深刻理解它。各位博友看到這里,可以自己試着寫一下上面的函數,然后再對比我寫的函數,如果你覺得你寫的函數更優雅,歡迎給我留言!
以下是我寫的算法,其實從有思路到寫出程序也廢了很大勁:

// 任意數分三組,每組和盡量相等(也就是最大值與最小值差值最小) function equal(arr) { var array = []; arr = sortMArray(arr); for (var i = 0; i < arr.length; i++) { // 轉換成多維數組 array.push([arr[i]]); } while (array.length > 3) { array[2].push(array[3][0]); array.splice(3, 1); array = sortMArray(array); } return array; }
為了按要求輸出數組,我還使用了多維數組排序及數組內元素求和的算法,我在此沒有深究,只是普通的冒泡排序,如下:
// 計算數組元素和 function sum(o) { var sum = 0; if (Array.isArray(o)) { o.map(function(item, index, array) { sum += item; }); } else { sum += o; } return sum; } // 多維數組排序 function sortMArray(arr) { for (var i = 0, len = arr.length; i < len - 1; i++) { for (var j = i + 1; j < len; j++) { if (sum(arr[i]) < sum(arr[j])) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } return arr; }
看着輸出的結果,確實有點小激動~
最后,我們可以將此題再延伸一下,比如任意數分任意組,有了上面的算法,這個就很簡單了,直接貼代碼
// 計算數組元素和 function sum(o) { var sum = 0; if (Array.isArray(o)) { o.map(function(item, index, array) { sum += item; }); } else { sum += o; } return sum; } // 多維數組排序 function sortMArray(arr) { for (var i = 0, len = arr.length; i < len - 1; i++) { for (var j = i + 1; j < len; j++) { if (sum(arr[i]) < sum(arr[j])) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } return arr; } // 任意數分 n 組,每組和盡量相等(也就是最大值與最小值差值最小) function equal(arr,n) { var array = []; arr = sortMArray(arr); for (var i = 0; i < arr.length; i++) { // 轉換成多維數組 array.push([arr[i]]); } while (array.length > n) { array[n-1].push(array[n][0]); array.splice(n, 1); array = sortMArray(array); } return array; }
// 測試數組 var arr = [3, 8, 20, 15, 60, 1, 32];
最后的最后還要說一句,也算是自我檢討以及對代碼的孜孜以求,雖然功能完成了,但從程序員的角度來看,我還沒有對輸入參數進行校驗,所以這樣的代碼還是有瑕疵的!