算法:小于n的最大数

算法题目

题目要求:给定一个数组例如nums=[2,3,4,5],n=2345,求使用nums中的数字,组成一个不大于N的最大的数字。nums中的数字可以多次使用。
备注:例如nums=[6, 8], n = 300。那么应该返回最大数字为88。

这个题目在网上有很多解答,但是大部分解答都有问题,没法完全的覆盖所有的case,因此在这里记录下我的解答。

思路与解答

思路: 贪心 + 二分
首先讲nums数组进行排序,注意下需要判断我们要寻找的最大值,如果n中的每一位数都比nums数组最小值都要小,那么我们要找寻的最大数长度为原来的n长度减一。
然后从左向右找寻n每一位在nums数组中的最大值,这里可以利用二分。在这个遍历过程中,注意后一位如果比nums数组中的最小值还要小,那么就应该这一位要找寻的最大值再减一。

/**
 * 从数组中选择元素组成小于n得最大整数
 */
public class Main{

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while(in.hasNextLine()){
            String[] s = in.nextLine().split(" ");
            int[] nums = new int[s.length];
            for(int i = 0; i < nums.length; i++){
                nums[i] = Integer.parseInt(s[i]);
            }

            int n = Integer.parseInt(in.nextLine());
            System.out.println(new Main().getMaxLessNum(nums, n));
        }
    }

    private int getMaxLessNum(int[] nums, int n){
        Arrays.sort(nums);
        int minNum = nums[0];
        String maxNum = getMaxString(minNum, n);
        StringBuilder s = new StringBuilder();
        boolean preIndexLess = false;
        for(int i = 0; i < maxNum.length(); i++){
            int index = preIndexLess ? nums.length - 1 : search(maxNum, i, nums);
            s.append(nums[index]);
            if(nums[index] < (maxNum.charAt(i) - '0')){
                preIndexLess = true;
            }
        }

        return Integer.parseInt(s.toString());
        
    }

    // 获取要查询的最大值, 可能出现拼接不出与原来数字长度相等的数,那么就返回长度减一的最大值
    private String getMaxString(int minNum, int n){
        boolean flag = false;

        String s = String.valueOf(n);
        // 解决前一个版本出现的bug
        for(char c : s.toCharArray()){
            if((c - '0') > minNum){
                flag = true;
                break;
            }else if ((c - '0') < minNum){
                break;
            }
        }

        int maxNum = flag ? (n - 1) : (int)(Math.pow(10, s.length() - 1) - 1);
        return String.valueOf(maxNum);
    }

    /**
     * 二分查找,查找等于或者小于findNum的最右边位置
     *
     */
    private int search(String maxNum, int index, int[] nums){
        int curMax = maxNum.charAt(index) - '0';
        int minNum = nums[0];
        int findNum = curMax;
        if(index < maxNum.length() - 1){
            findNum = (maxNum.charAt(index + 1) - '0') < minNum ? curMax - 1 : curMax;
        }

        int left = 0, right = nums.length - 1;
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(nums[mid] > findNum){
                right = mid - 1;
            }else if(nums[mid] < findNum){
                left = mid + 1;
            }else{
                return mid;
            }
        }

        return right;
    }
}

时间复杂度:O(nlogn)

参考

这有篇更详细的:https://blog.csdn.net/m0_50043893/article/details/126543373

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值