[剑指Offer] 45_把数组排成最小的数

本文介绍了一种算法,用于将数组中的所有数字拼接成最小的可能数字。通过比较'a'+ 'b'和'b'+ 'a'的大小,可以确定数字的正确顺序,从而避免了全排列的高时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

例:

输入数组{3, 32, 321}, 打印321323。


思路

  1. 排列出所有的组合,比较出最小的,输出。
    1. 时间复杂度:O(n!)
    2. 空间复杂度:O(n!)
  2. 考察最小组合的形式,遵从下面的规律:1、首位小的靠前 2、首位相同,次位小的靠前 3、位数少的靠前。因此等价于比较a + b 和 b + a。下面证明以’a’ + ‘b’ 和 ‘b’ + ‘a’ 的大小顺序排列在多个数字时仍然成立。假设’a’ + ‘b’ 和 ‘b’ + ‘a’。下面证明以’a’ + ‘b’ 和 ‘b’ + ‘a’ 的大小顺序排列在多个数字时仍然成立。假设ab > ba,说明’a’ > ‘b’,则若 bc > cb,‘b’ > ‘c’,有: a ∗ 1 0 D b + b > b ∗ 1 0 D a + a a*10^{Db} + b > b*10^{Da} + a a10Db+b>b10Da+a,可得 a / ( 1 0 D a − 1 ) > b / ( 1 0 D b − 1 ) a/(10^{Da} -1) > b/(10^{Db}-1) a/(10Da1)>b/(10Db1),同理, b / ( 1 0 D b − 1 ) > c / ( 1 0 D c − 1 ) b/(10^{Db} -1) > c/(10^{Dc}-1) b/(10Db1)>c/(10Dc1),因此 a / ( 1 0 D a − 1 ) > c / ( 1 0 D c − 1 ) a/(10^{Da} -1) > c/(10^{Dc}-1) a/(10Da1)>c/(10Dc1),所以ac > ca。比较具有传递性。因此只要将普通排序中比较两个元素大小的关系,用字符串比较替代即可。
    1. 时间复杂度:O(nlogn)使用快排
    2. 空间复杂度:O(1)

代码

思路2:时间复杂度:O(nlogn),空间复杂度:O(1)

def sort_array_for_min_number(nums):
    """
    :param nums: int list
    :return: min number string
    """
    from functools import cmp_to_key
    nums = [str(i) for i in nums]
    nums.sort(key = cmp_to_key(lambda a,b:-1 if a + b < b + a else 1))
    return ''.join(nums).lstrip('0') or '0'

思考

cmp参数、key参数

python2
list.sort(cmp=None, key=None, reverse=False)

python3
list.sort( key=None, reverse=False)

Python2

list.sort(cmp=None, key=None, reverse=False)
参数
cmp – 可选参数, 如果指定了该参数会使用该参数的方法进行排序。
key – 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse – 排序规则,reverse = True 降序, reverse = False 升序(默认)。

在Python3中没有cmp参数,需要借助functools.cmp_to_key,将用于比较的func转换成key参数需要的函数,在key参数中实现cmp参数的功能。

def cmp_to_key(mycmp):
    """Convert a cmp= function into a key= function"""
    class K(object):
        __slots__ = ['obj']
        def __init__(self, obj):
            self.obj = obj
        def __lt__(self, other): # less than
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other): # greater than
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other): # equal
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other): # less equal
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):# greater equal
            return mycmp(self.obj, other.obj) >= 0
        __hash__ = None
    return K

cmp_to_key函数将自定义的cmp函数转换成了一个类K,而K具有使用mycmp函数规定的比较的5种内置方法。list.sort(key = mykey)时,比较a、b两个元素时,实际比较的是mykey(a)、mykey(b)这两个类K的实例,根据比较符号是<、>、==、<=、>=分别调用__lt__、__gt__、__eq__、__le__、__ge__内置方法。此时结果就会是mycmp(a, b)。
因此,在构造mycmp时,我们希望’a’ + ‘b’ >= ‘b’ + 'a’时,mycmp(a, b) > 0 == True。
注意,此处使用lambda可以使运行速度更快。

类似题目

LeetCode 179. 最大数

题目

给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。

示例 1:

输入: [10,2]
输出: 210

示例 2:

输入: [3,30,34,5,9]
输出: 9534330

说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。

代码

class Solution:
    def largestNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """
        from functools import cmp_to_key
        nums = [str(num) for num in nums]
        nums.sort(key = cmp_to_key(lambda a,b: 1 if a+b<b+a else -1))
        return ''.join(nums).lstrip('0') or '0'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值