剑指 Offer 45. 把数组排成最小的数【17】

难度等级:中等

上一篇算法:

(33条消息) 剑指 Offer 42. 连续子数组的最大和【数组】_Java运动猿的博客-CSDN博客

力扣此题地址:

剑指 Offer 45. 把数组排成最小的数 - 力扣(LeetCode)

1.题目:剑指 Offer 45. 把数组排成最小的数

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

示例 :

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

2.解题思路:

利用快速排序+递归思想

1.先将整型数组转为字符串数组(防止数字过大溢出)

2.再用快速排序、递归的思想,定一个pivot基准值,分出left、right两个区域,递归不断分下去,将左右两块区域的元素移动,最终另左边任意元素与pivot组合都小于pivot在前,右边任意元素与pivot组合都大于pivot在前。

3.代码实现:

class Solution {
    public String minNumber(int[] nums) {

        // 先将 nums 转换为字符串数组的形式
        String[] strs = new String[nums.length];

        for(int i = 0; i < nums.length; i++){
            strs[i] = String.valueOf(nums[i]);
        }
        // 通过快速排序的方式,将字符串数组的每个字符按照约定的顺序进行排序
        quickSort(strs, 0, strs.length - 1);

        // 再把字符串数组转字符串的形式
        StringBuilder ans = new StringBuilder();

        for(String s : strs)
            ans.append(s);

        return ans.toString();
    }

    // 函数传入待排序数组 nums
    // 排序区间的左端点 left
    // 排序区间的右端点 right
    private void quickSort(String[] strs,int left, int right){

        // 如果 left 大于等于 right,说明该区间只有 1 个或者没有元素
        if( left >= right ){
            // 无需再递归划分后再排序,直接返回
            return;
        }

        // 调用函数 partition,将 left 和 right 之间的元素划分为左右两部分
        int mid = partition(strs,left,right);

        // 划分之后,再对 mid 左侧的元素进行快速排序
        quickSort(strs,left,mid - 1);

        // 划分之后,再对 mid 右侧的元素进行快速排序
        quickSort(strs,mid + 1,right);
    }

    // 直接套之前的快速排序的代码进行修改
    // 原先的小于的含义指的是数值上的小于,比如 1  < 10 
    // 但现在的小于含义为:a + b 拼凑的字符串小于 b + a 拼凑的字符串
    // 比如 a = 1 ,b = 10 
    // 那么 a + b = “110”,b + a = “101”
    // 显然,b + a < a + b
    // 也就是说 a 应该放到 b 的后面来拼凑字符串
    private int partition(String[] strs, int left ,int right){

        // 经典快速排序的写法
        // 设置当前区间的第一个元素为基准元素
        String pivot = strs[left];

        // left 向右移动,right 向左移动,直到 left 和 right 指向同一元素为止
        while( left < right ){

            // 当 pivot + strs[right] 的字符串小于 strs[right] + pivot 的字符串时
            // 说明 strs[right] 在正确的位置上,right 向左移动
            while( left < right && (pivot + strs[right]).compareTo(strs[right] + pivot) <= 0 ){
                // right 不断的向左移动
                right--;
            }

            // 此时,跳出了上面这个 while 循环,说明 pivot + strs[right] 的字符串大于 strs[right] + pivot 的字符串了
            // 说明 strs[right] 不在正确的位置上
            // 将此时的 strs[left] 赋值为 strs[right]
            // 执行完这个操作,比 pivot 小的这个元素被移动到了左侧
            strs[left] = strs[right];

            // 当 strs[left] + pivot 的字符串小于 pivot + strs[left] 的字符串时
            // 说明 strs[left] 在正确的位置上,left 向右移动
            while( left < right && (strs[left] + pivot).compareTo(pivot + strs[left]) <= 0){
                // left 不断的向右移动
                left++;
            }

            // 此时,跳出了上面这个 while 循环,说明 strs[left] + pivot 的字符串大于 pivot + strs[left] 的字符串了
            // 说明 strs[left] 不在正确的位置上
            // 将此时的 strs[right] 赋值为 strs[left]
            // 执行完这个操作,比 pivot 大的这个元素被移动到了右侧
            strs[right] = strs[left];

        }

        // 此时,left 和 right 相遇,那么需要将此时的元素设置为 pivot
        // 这个时候,pivot 的左侧元素都小于它,右侧元素都大于它
        strs[left] = pivot;

        // 返回 left
        return left;

    }
}

4.参考:

剑指 Offer 45. 把数组排成最小的数_AlgoMooc算法慕课网

快速排序算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值