【算法-LeetCode】179. 最大数(Array.sort())

179. 最大数 - 力扣(LeetCode)

发布:2021年9月27日20:57:12

问题描述及示例

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例 1:
输入:nums = [10,2]
输出:“210”

示例 2:
输入:nums = [3,30,34,5,9]
输出:“9534330”

示例 3:
输入:nums = [1]
输出:“1”

示例 4:
输入:nums = [10]
输出:“10”

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

提示:
1 <= nums.length <= 100
0 <= nums[i] <= 109

我的题解

成功前的尝试

第一反应是先利用全排列的思想把所有可能的排列结果都给遍历出来,然后根据再从所有的可能结果中筛选出组成的值最大的那个作为答案返回即可。

/**
 * @param {number[]} nums
 * @return {string}
 */
var largestNumber = function(nums) {
  let result = 0;
  let temp = [];
  backtracking(nums, []);
  return result.toString();

  function backtracking(nums, used) {
    if(temp.length === nums.length) {
      result = Math.max(result, Number(temp.join('')));
      return;
    }
    for(let i = 0; i < nums.length; i++) {
      if(used[i]) {
        continue;
      }
      temp.push(nums[i]);
      used[i] = true;
      backtracking(nums, used);
      used[i] = false;
      temp.pop();
    }
  }
};


执行结果:解答错误
通过测试用例:47 / 230
输入:[999999998,999999997,999999999]
输出:"1e+27"
预期结果:"999999999999999998999999997"
时间:2021/09/27 20:59

更新:2021年10月2日17:38:49

这道题目之前做了一点,想到了这种全排列的思路,但是发现没法儿通过,后来也没想到好的点子,于是存在草稿箱里了,但是后来给忘了给补上了,今天就把这篇尘封多日的博客给补完吧。

然而,我还是太年轻了,忽视了一个重要的因素,那就是JavaScript中的数字表示范围是有局限的:

在这里插入图片描述

JavaScript不能精确表示大数

可以看到,在JavaScript中,如果整数超过了20位,那么就会用科学计数法来表示,而且也不能精确地表示原来的数,而且当把这个科学计数法表示的数字转化为字符串时也会是科学计数法。在JavaScript中,能够精确表示的最大数字就是 Number.MAX_SAFE_INTEGER 这个常数。

MAX_SAFE_INTEGER 是一个值为 9007199254740991 的常量。

—— JavaScript | MDN

参考:Number.MAX_SAFE_INTEGER - JavaScript | MDN

当然,如果可以像之前做的全排列类型的题目一样在下面这个 if 判断中增添一些限制条件已达到提前“剪枝”的效果,可能可以用全排列的思路来做:

if(used[i]) {
  continue;
}

但是,我想了很久都没想出来该如何增加判断条件……所以说,用全排列的思路来做还是没法儿实现效果的……于是我借助了【官方题解】。

我的题解(数组排序/Array.sort()函数)

准确来说,这个题解虽然和【官方题解】稍有不同,但确实是根据【官方题解】的思路做的变装。

总体思路也非常简单,其关键就是利用了 JavaScript 中的 Array.sort() 函数。所以在理解下面的解法之前,可以先思考一下 sort() 函数的原理:

参考:【微信公众号:高级前端进阶 2021-09-23】V8 sort是快排吗,如何实现一个 Array.prototype.sort

参考:【JavaScript】数组的sort方法排序原理详解_汪小穆的博客-CSDN博客_数组sort排序原理

参考:关于js数组sort方法的实现原理总结_黯然销魂者-CSDN博客

参考:js中 sort 方法的使用 和 底层实现原理_SuperStition9的博客-CSDN博客

整体思路抽象起来就是三步:

  1. 制定好 sort() 函数中的回调函数的比较规则。
  2. 根据上面制定好的规则,调用 sort() 函数对 nums 数组进行排序。
  3. 把最后的排序结果拼接为一个字符串作为返回值返回。

关键就是 sort() 函数里的回调规则。这里就要利用【官方题解】中提到的提到的这种排序规则的充分必要性。具体的过程推导可以看【官方题解】中的相关描述。

简单地概括排序规则就是:假设数组中的 ab 两个数字拼接之后得到的组合数字有 ab > ba,那么就可以确定在最后的排序结果中,a 一定排在 b 的前面。

那么根据上面的排序规则,再结合 sort() 函数的原理,就可以通过比较 nums两两结合的数字的大小来确定最后的排序结果。而其中的“两两结合”就是关键。

下面程序中的 combination1combination2 就是分别是 ab 中的数字两种排序结果。然后只要根据这两种排序的大小比较结果就能判断当前的两个元素是否需要交换位置,这就是 sort() 中的回调函数的逻辑。

我写的这种回调的排序规则的实现方式和【官方题解】的不大一样,我感觉【官方题解】中的那种思路不是很直接,所以就写出了这种解法。

/**
 * @param {number[]} nums
 * @return {string}
 */
var largestNumber = function(nums) {
  nums.sort((a, b) => {
    let combination1 = a.toString() + b.toString();
    let combination2 = b.toString() + a.toString();
    return Number(combination2) - Number(combination1);
  });
  // 这里不能简单地写成return nums.join(''),而要加一个判断,详情请看下方的【补充】
  return nums[0] === 0 ? '0' : nums.join('');
};

提交记录
230 / 230 个通过测试用例
状态:通过
执行用时:68 ms, 在所有 JavaScript 提交中击败了91.62%的用户
内存消耗:39.6 MB, 在所有 JavaScript 提交中击败了34.90%的用户
时间:2021/10/02 17:15

补充
如果这里不加上这个判断的话,那碰到 nums=[0,0] 这个用例时,就会与预期不符:

在这里插入图片描述

return 没加判断时的后果

可以这么理解,如果降序排序之后,第一个元素仍为 0 (题目中已经说明了 nums 中的数字都是非负整数),那么就可以断定 nums 中的所有元素都是 0,那么这些元素组合起来的最大数就为单位的 0,而不是多位的 000 之类的结果。

官方题解

更新:2021年7月29日18:43:21

因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。

更新:2021年10月2日18:19:16

参考:最大数 - 最大数 - 力扣(LeetCode)

【更新结束】

有关参考

更新:2021年10月1日17:20:45
参考:Number.MAX_SAFE_INTEGER - JavaScript | MDN
更新:2021年10月2日17:29:57
参考:JS 因数值过大变为科学计数法,科学计数法转换为正常数值_yangzi_520的博客-CSDN博客

【Array.sort() 函数的原理】
参考:【微信公众号:高级前端进阶 2021-09-23】V8 sort是快排吗,如何实现一个 Array.prototype.sort
参考:【JavaScript】数组的sort方法排序原理详解_汪小穆的博客-CSDN博客_数组sort排序原理
参考:关于js数组sort方法的实现原理总结_黯然销魂者-CSDN博客
参考:js中 sort 方法的使用 和 底层实现原理_SuperStition9的博客-CSDN博客

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值