JavaScript中sort方法的一個坑(leetcode 179. Largest Number)


在做 Largest Number 這道題之前,我對 sort 方法的用法是非常自信的。我很清楚不傳比較因子的排序會根據元素字典序(字符串的UNICODE碼位點)來排,如果要根據大小排序,需要傳入一個比較函數。

先來看這道題,給你一個數組,讓你把數組元素拼接起來,求能拼得的最大的數。如果只有兩個數字 a 和 b,如何拼?很明顯比較 abba 兩個數的大小,所以這道題首先需要對數組做一次排序。刷刷寫下如下代碼:

nums.sort(function(a, b) {
  return (b + '' + a) > (a + '' + b);
});

因為 baab 位數相同,我覺得根據字典序即可比較大小,提交,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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM