[2021校招必看之Java版《剑指offer》-32] 把数组排成最小的数

1、题目描述

  【JZ32】输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
  知识点:数组,全排列,排序,贪心
  难度:☆

2、解题思路

2.1 暴力计算

  核心思想:拼接出所有可能的组合,然后从这些组合中选择最小的。

  于是任务变成两个:1、拼接所有可能的组合;2、找出最小值。

  拼接所有可能的组合,即全排列。全排列可以使用递归的方法,数组的每一个元素都当放一次在首位,然后数组剩下的元素继续全排列。当数组只有一个元素,那么全排列就是它本身。

  初始步骤如下:
  1、定义两个指针 start 和 end,数组的 [start, end] 范围内的元素是待全排列的元素;
  2、初始时:start = 0,end = array.length - 1,即一开始需要全排列的元素是所有元素;
  3、接下来开始递归:
  3.1 递归函数的功能:求解 array 的 [start, end] 区间元素的全排列;
  3.2 递归结束条件:start == end;
  3.3 不断缩减 [start, end] 区间大小,即:每一次递归,start++。

  把所有的全排列都保存在 ArrayList<String> 中,然后从中找出最小的。

2.2 贪心算法

  贪心算法是局部最优解,我们知道,局部最优解不一定能得到全局最优解,但是,也不一定得不到全局最优解。

  对于本题这种情况,是适合使用贪心算法,贪心算法得出的解就是最优解,至于为什么,可以查看相应的数学证明,查看什么情况下贪心算法的解等于最优解。

  对于两个字符串 a 和 b,如果 a + b < b + a,那么 a + b 就是最小值,即我们偏向于把 a 放在前边。

  基于这种情况,我们首先把 array 数组从小到大排列,然后从小到大两个元素依次比较。

  步骤如下:
  1、把 array 从小到大排列;
  2、定义一个 String 类型的变量 result,初始值为:String.valueOf(array[0]);
  3、从第 1 个元素开始,遍历 array,如果 result + String.valueOf(numbers[i]) < String.valueOf(numbers[i]) + result,则 result = result + String.valueOf(numbers[i]),反之 result = String.valueOf(numbers[i]) + result。
  4、遍历完毕,返回 reuslt。

3、解题代码

3.1 暴力计算

package pers.klb.jzoffer.hard;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * @program: JzOffer2021
 * @description: 把数组排成最小的数
 * @author: Meumax
 * @create: 2020-07-25 09:59
 **/
public class PrintMinNumber {

    public String PrintMinNumber(int[] numbers) {
        if (numbers.length == 0) return "";
        ArrayList<String> list = new ArrayList<>();
        permutation(numbers, 0, numbers.length - 1, list);

        String min = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i).compareTo(min) < 0) {
                min = list.get(i);
            }
        }

        return min;
    }

    private void permutation(int[] array, int start, int end, ArrayList<String> list) {
        if (start == end) {
            String s = "";
            for (int num : array) {
                s += String.valueOf(num);
            }
            list.add(s);
        } else {
            for (int i = start; i <= end; i++) {
                swap(array, start, i);  // array[start] 和 array[i] 交换,即让每一个元素都当一次首元素
                permutation(array, start + 1, end, list);
                swap(array, start, i);  // 恢复 array
            }
        }
    }

    private void swap(int[] array, int index1, int index2) {
        int temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
    }
}

3.2 贪心算法

package pers.klb.jzoffer.hard;

import java.util.Arrays;

/**
 * @program: JzOffer2021
 * @description: 把数组排成最小的数
 * @author: Meumax
 * @create: 2020-07-25 09:59
 **/
public class PrintMinNumber {

    public String PrintMinNumber(int[] numbers) {
        if (numbers.length == 0) return "";
        Arrays.sort(numbers);   // 对数组从小到大排序
        String result = String.valueOf(numbers[0]);
        for (int i = 1; i < numbers.length; i++) {
            result = joint(result, String.valueOf(numbers[i]));
        }
        return result;
    }

    private String joint(String s1, String s2) {
        return (s1 + s2).compareTo(s2 + s1) < 0 ? (s1 + s2) : (s2 + s1);
    }
}

4、解题心得

  本题难度适中,考察贪心算法的使用,对于某些情况下,贪心算法的解就是最优解,但是什么情况下成立,得查阅详细的数学证明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值