在做 Largest Number 這道題之前,我對 sort 方法的用法是非常自信的。我很清楚不傳比較因子的排序會根據元素字典序(字符串的UNICODE碼位點)來排,如果要根據大小排序,需要傳入一個比較函數。
先來看這道題,給你一個數組,讓你把數組元素拼接起來,求能拼得的最大的數。如果只有兩個數字 a 和 b,如何拼?很明顯比較 ab 和 ba 兩個數的大小,所以這道題首先需要對數組做一次排序。刷刷寫下如下代碼:
nums.sort(function(a, b) {
return (b + '' + a) > (a + '' + b);
});
因為 ba 和 ab 位數相同,我覺得根據字典序即可比較大小,提交,221 組數據跪在了 201 組。我開始懷疑算法的正確性,直到無意中把比較函數改了下,便 AC 了:
nums.sort(function(a, b) {
return (b + '' + a) - (a + '' + b);
});
這真的有區別么?印象中 c++ 中調用 STL 的 sort 函數,在比較函數中一直用的 < 以及 > 啊。我承認我以前從沒有留意過。
我們把問題簡化,先來看以下代碼:
var largestNumber = function(nums) {
nums.sort(function(a, b) {
return a - b;
});
return nums;
};
console.log(largestNumber([41,32,81,97,99,91,90,63,76,76,67]));
// [32, 41, 63, 67, 76, 76, 81, 90, 91, 97, 99]
這是一次升序排序,結果完美,那么,為什么能得到這樣的結果,有思考過 return a - b
的意思么?
如果指明了 compareFunction ,那么數組會按照調用該函數的返回值排序。記 a 和 b 是兩個將要被比較的元素:
- 如果 compareFunction(a, b) 小於 0 ,那么 a 會被排列到 b 之前;
- 如果 compareFunction(a, b) 等於 0 , a 和 b 的相對位置不變。
- 如果 compareFunction(a, b) 大於 0 , b 會被排列到 a 之前。
上面的的代碼其實可以擴寫成這樣:
var largestNumber = function(nums) {
nums.sort(function(a, b) {
if (a - b > 0)
return 1; // a 比 b 大,b 排到 a 前
else if (a - b < 0)
return -1; // a 比 b 小,a 排到 b 前
else
return 0; // a 和 b 一樣大,a 和 b 相對位置不變
});
return nums;
};
仔細想想,這樣便是一個升序排列。
再來看錯誤寫法:
var largestNumber = function(nums) {
nums.sort(function(a, b) {
return a > b;
});
return nums;
};
console.log(largestNumber([41,32,81,97,99,91,90,63,76,76,67]));
// [91, 41, 32, 63, 67, 76, 76, 81, 90, 97, 99]
其實可以擴寫為:
var largestNumber = function(nums) {
nums.sort(function(a, b) {
if (a > b)
return 1; // true 隱式轉換為 1; b 排到 a 前
else
return 0; // false 隱式轉換為 0; 相對位置不變
});
return nums;
};
仔細想想,明顯有問題。a > b 時 b 排到 a 前沒有問題,a < b 時位置不變,這就不對了。如果 a < b 時 return -1,讓 a 排到 b 前,就對了。所以這樣寫也是沒問題的:
var largestNumber = function(nums) {
nums.sort(function(a, b) {
if (a > b)
return 1;
else
return -1;
});
return nums;
};
當然,a === b 這個條件可以隨意歸到 a > b 或者 a < b 那,也可以拎出來獨立,以上代碼我是歸到 a < b 那了。
另外還需要注意的一點是,比較函數中的 a 和 b 參數,在數組中的位置是任意的,並不是 a 前 b 后。這里我寫了段測試代碼:
var largestNumber = function(nums) {
nums.sort(function(a, b) {
console.log(a, b);
console.log(nums.concat());
return a - b;
});
return nums;
};
console.log(largestNumber([41,32,81,97,99,91,90,63,76,76,67]));
結果很明顯:
最后勉強回憶起 C++ 中的 sort 的比較函數返回的是一個 bool 值,而 JavaScript 比較函數能返回三個值,跟 C++ 中的 qsort 類似?實在回憶不起來也不想回憶了。
本題完整代碼可以移步 https://github.com/hanzichi/leetcode/tree/master/Algorithms/Largest Number。PS:還有一個坑,拼起來的字符串需要去前導 0。