【LeetCode】剑指 Offer 45. 把数组排成最小的数 p227 -- Java Version

题目链接:https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/

1. 题目介绍(45. 把数组排成最小的数)

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

【测试用例】:
示例1:

输入: [10,2]
输出: “102”

示例2:

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

【条件约束】:

提示:

  • 0 < nums.length <= 100

……
说明:
输出结果可能非常大,所以你需要返回一个字符串而不是整数
拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0

2. 题解

2.1 枚举 – (n!)

时间复杂度O(n!),空间复杂度O(n)

解题思路】:
这道题最直接的解法就是先求出这个数组中所有数字的全排列,然后把每个排列拼起来,最后求出拼起来的数字的最小值。求数组的全排列,可参考【LeetCode】剑指 Offer 38. 字符串的排列 p197 – Java Version,根据排列组合的知识,n个数字总共有 n! 个排列
……
实现策略】:
枚举实现主要分为三步:

  1. 将整型数组 nums 转为 字符串数组 strings,这一步我们是通过 for 循环遍历转换每一个数组值实现;
  2. 对字符串数组中的元素进行全排列,使用了 剑指 Offer 38. 字符串的排列 中的回溯方法,对字符串数组进行了全排列,同时通过 HashSet 去掉了重复数据;
  3. 排列完毕,通过库函数 Collections.sort() 对字符串数组进行排序,排序完毕后,字符串数组的第一个元素即为最小元素。

……
与剑指 Offer 38 中排列方法的区别】:
与剑指 Offer 38题中不同的是,该题是整型数组,且数字可能有多位,因此不能再使用 38题 中的字符数组了。因而,在本题中,字符数组被改成了字符串数组,最后将添加排列结果到集合中时,还需要拼接一下字符串数组中的所有元素,过程可见 concatAllString() 方法。

class Solution {
    
    
    // Solution1:枚举

    // 结果字符串存储
    List<String> res = new LinkedList<>();

    // 暂时存储
    String[] strings;

    public String minNumber(int[] nums) {
    
    
        // 无效输入判断
        if (nums.length <= 0) return null;

        // 1. 将整型数组变为字符串数组
        strings = new String[nums.length];
        for (int i = 0; i < nums.length; i++) {
    
    
            strings[i] = Integer.toString(nums[i]); 
        }
        // 2. 对字符串数组中的元素进行全排列
        dfs(0); 
        // 3. 排列完毕,直接排序,返回最小
        Collections.sort(res);
    
        return res.get(0);
    }

    public void dfs(int idx){
    
    
        // 递归终止条件: 当前字符数组固定完毕
        if (idx == strings.length-1){
    
    
            // 将该字符串存入结果列表
            res.add(concatAllString(strings));
            // 结束当前递归
            return;
        }

        // 利用HashSet给重复字符串的情况进行去重
        HashSet<String> set = new HashSet<>();
        for (int i = idx; i < strings.length; i++){
    
    
            // 判重,如果有重复的字符,则跳过当前循环,不进行记录
            if (set.contains(strings[i])) continue;
            set.add(strings[i]);
            swap(i,idx);
            dfs(idx+1);
            swap(i,idx);
        }


    }
    // 交换字符数组的两个字符的位置
    public void swap(int a, int b){
    
    
        String temp = strings[a];
        strings[a] = strings[b];
        strings[b] = temp;
    }

    // 拼接当前字符串数组中的所有数据
    public String concatAllString(String[] strings) {
    
    
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < strings.length; i++) {
    
    
            sb.append(strings[i]);
        }
        return sb.toString();
    }

}

在这里插入图片描述

2.2 库函数排序 – O(nlogn)

时间复杂度O(nlogn),空间复杂度O(n)

解题思路】:
该题主要是考察排序算法,排序规则是两个字符串str1,str2,如果str1+str2 > str2+str1,说明str1大于str2。

  1. nums 中每个num转为字符串存到数组中,
  2. 自定义 Array.sort() 方法,使用字符串的 compareTo 方法判断两个相加的字符串大小。

……
补充】:

  • compareTo 方法返回值为正则正序排序,返回值为负,则倒序排序
    排序原理可参考:compare排序原理
class Solution {
    
    
    // Solution2:重写Comparator
    public String minNumber(int[] nums) {
    
    
        int n = nums.length, i = 0;
        String[] numsStr = new String[n];
        for (int num : nums){
    
    
            numsStr[i++] = String.valueOf(num); //将nums转为字符串
        }
        Arrays.sort(numsStr, new Comparator<String>() {
    
    
            @Override
            public int compare(String o1, String o2) {
    
    
                //如果o1+o2 > o2+o1,说明o1大于o2,返回1,交换二者位置
                if ((o1+o2).compareTo(o2+o1) > 0)   
                    return 1;
                return -1;
            }
        });
        String ans = "";
        for (String numstr : numsStr)
            ans += numstr;
        return ans;
    }
}


在这里插入图片描述
代码简化

解题思路】:

  • 该题解使用了 lambda 表达式简化了重写过程,用 StringBuilder 也提高了字符串拼接的效率。
class Solution {
    
    
    public String minNumber(int[] nums) {
    
    
        String[] strs = new String[nums.length];
        for(int i = 0; i < nums.length; i++)
            strs[i] = String.valueOf(nums[i]);
        Arrays.sort(strs, (x, y) -> (x + y).compareTo(y + x));
        StringBuilder res = new StringBuilder();
        for(String s : strs)
            res.append(s);
        return res.toString();
    }
}


在这里插入图片描述
小根堆

class Solution {
    
    
    public String minNumber(int[] nums) {
    
    
        Queue<String> queue = new PriorityQueue<>(new Comparator<String>() {
    
    
            @Override
            public int compare(String o1, String o2) {
    
    
                //字典序列小的放在堆顶
                return (o1 + o2).compareTo(o2 + o1);
            }
        });
        for (int num : nums) {
    
    
            queue.add("" + num);
        }
        StringBuilder res = new StringBuilder();
        while (! queue.isEmpty()){
    
    
            res.append(queue.poll());
        }
        return res.toString();
    }
}

在这里插入图片描述

2.3 快排 – O(nlogn)

时间复杂度O(nlogn),空间复杂度O(n)

解题思路】:
本题求拼接起来的 “最小数字” ,本质上其实是一个排序问题。 排序判断规则: 设 nums任意两数字的字符串格式 xy,则 若拼接字符串 x + y > y + x,则 m > n; 反之,若 x + y < y + x,则 n < m; 根据以上规则,套用任何排序方法对 nums执行排序即可
……
实现策略】:
以下是使用快排的解法

class Solution {
    
    
    public String minNumber(int[] 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 res = new StringBuilder();
        for(String s : strs)
            res.append(s);
        return res.toString();
    }
    void quickSort(String[] strs, int l, int r) {
    
    
        if(l >= r) return;
        int i = l, j = r;
        String tmp = strs[i];
        while(i < j) {
    
    
            while((strs[j] + strs[l]).compareTo(strs[l] + strs[j]) >= 0 && i < j) j--;
            while((strs[i] + strs[l]).compareTo(strs[l] + strs[i]) <= 0 && i < j) i++;
            tmp = strs[i];
            strs[i] = strs[j];
            strs[j] = tmp;
        }
        strs[i] = strs[l];
        strs[l] = tmp;
        quickSort(strs, l, i - 1);
        quickSort(strs, i + 1, r);
    }
}

在这里插入图片描述
更多排序方法可参考:写一下常见的排序算法吧!!!

3. 参考资料

[1] 剑指 Offer 45. 把数组排成最小的数(自定义排序,清晰图解)
[2] java重写Comparator,代码简洁明了

猜你喜欢

转载自blog.csdn.net/qq_41071754/article/details/129950245