字节面试题-组成小于n的最大整数

在这里插入图片描述

先举例子说说我的思路
如题
1,2,4,9
目标值是2533

先对数据从大到小取出
第一个数字是9,没问题,然后计算一下后面最多还能拼接多少个数,
因为9开头,所以最大能取出的就是三位数了,因此只剩下2个名额拼接数字了(9比目标值的最高位2要大,所以最终值要比目标值少1位长度),
然后递归,下一次又拼接一个9,然后就剩下最后1个名额了,然后再递归,就发现没名额了,就需要退出递归了
(说那么多废话,就是想告诉你们,我这里是排列组合,然后借助剩余的名额是否等于0去结束递归)

上面是整体的实现思路,结果一跑,小数值还行,大数值从昨天跑到今天都没跑出来

排列组合太可怕了~

我后面想到了剪枝,具体剪枝看代码中的注释吧~

用了剪枝之后,输入再大的值,都是瞬间出结果~真舒服

不要害怕下面的代码!!!下面有一半都是注释!!!怕大家伙看的吃力!!也怕我忘了。。
最后我附上一版没有那么多注释的

public class combinationMaxNum {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.addAll(Arrays.asList(0,1,2,3,4,5,6,7,8,9));
        System.out.println(getMaxNum(list,Integer.MAX_VALUE));
    }
    public static Integer getMaxNum(List<Integer> list,Integer n){
        return maxNum(list, String.valueOf(n),String.valueOf(n).length(), "",new HashMap<>(),Integer.MIN_VALUE);
    }
    /**
     *
     * @param list 0-9数组
     * @param n  目标值
     * @param size  当前 nowValue 下最大还能放多少个数字
     * @param nowValue 组合的数字
     * @param map 用于剪枝,不然会超时,而且那就是纯纯的排列组合了
     * @param maxValue 存储最大值用的
     * @return
     */
    public static Integer maxNum(List<Integer> list,String n,Integer size,String nowValue,HashMap<Integer,Integer> map,Integer maxValue){
        if(size==0){
            return Integer.valueOf(nowValue);
        }
        for (int i = list.size()-1; i >= 0; i--) {
            String value = nowValue+list.get(i);
            if(value.compareTo(n)>=0){
                //当前值大于等于目标值,就说明已经超出大小了,就不要了
                continue;
            }
            final Integer otherSize = getOtherSize(n, value);
            //重点是这个剪枝!
            //没有这个剪枝的话,代码一样能跑,但是那就是排列组合所有可能了,直接超时
            //那到底剪掉了哪些肯定不是最优答案的递归呢?
            //例如说n=9001时,当我循环第二次组合出了一个 99(value),那otherSize就只有1了,也就是最大值就是999了,
            //那请问,98以及后面循环和递归的还需要么?
            //很明确就已经不需要了, 因为98的otherSize也是1了,无论后面再拼接什么,都不会再大于99开头的三位数了,
            //所以这个剪枝用的map的键是otherSize,值是剩余otherSize时拼接的最大的值;怎么存储的移步到下面的map.put();
            if(map.containsKey(otherSize)&&map.get(otherSize)<list.get(i)){
                //剪枝
                break;
            }
            //这里就是递归做组合,不详细说了
            int max = maxNum(list,n,otherSize,value,map,maxValue);
            //为什么要判断max!=Integer.MIN_VALUE?
            //因为例如9000的时候,当组合的数值到达9000后,发现等于目标值n了,因此没有结果返回,递归返回的max就是默认值,也就是Integer.MIN_VALUE
            //因此这里需要专门将这种情况过滤掉
            if(max!=Integer.MIN_VALUE){
                //这里就是存储otherSize和拼接的最大的值的地方了
                map.put(otherSize,list.get(i));
            }
            maxValue = Math.max(max,maxValue);
        }
        return maxValue;
    }

    //用于计算组合最新值后还可以再放多少个数字
    public static Integer getOtherSize(String n,String nowValue){
        if(n.compareTo(nowValue)>=0){
            //如果n目标值比现在的值大,返回长度差
            return n.length() - nowValue.length();
        }
        //如果n目标值比现在的值大,返回长度差再减1
        return n.length() - nowValue.length()-1;
    }
}

简化注释后的代码,核心方法maxNum 就十几行代码

public class combinationMaxNum {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.addAll(Arrays.asList(0,1,2,3,4,5,6,7,8,9));
        System.out.println(getMaxNum(list,Integer.MAX_VALUE));
    }
    public static Integer getMaxNum(List<Integer> list,Integer n){
        return maxNum(list, String.valueOf(n),String.valueOf(n).length(), "",new HashMap<>(),Integer.MIN_VALUE);
    }
    /**
     *
     * @param list 0-9数组
     * @param n  目标值
     * @param size  当前 nowValue 下最大还能放多少个数字
     * @param nowValue 组合的数字
     * @param map 用于剪枝,不然会超时,而且那就是纯纯的排列组合了
     * @param maxValue 存储最大值用的
     * @return
     */
    public static Integer maxNum(List<Integer> list,String n,Integer size,String nowValue,HashMap<Integer,Integer> map,Integer maxValue){
        if(size==0){
            return Integer.valueOf(nowValue);
        }
        for (int i = list.size()-1; i >= 0; i--) {
            String value = nowValue+list.get(i);
            if(value.compareTo(n)>=0){
                continue;
            }
            final Integer otherSize = getOtherSize(n, value);
            if(map.containsKey(otherSize)&&map.get(otherSize)<list.get(i)){
                //剪枝
                break;
            }
            int max = maxNum(list,n,otherSize,value,map,maxValue);
            if(max!=Integer.MIN_VALUE){
                map.put(otherSize,list.get(i));
            }
            maxValue = Math.max(max,maxValue);
        }
        return maxValue;
    }

    //用于计算组合最新值后还可以再放多少个数字
    public static Integer getOtherSize(String n,String nowValue){
        if(n.compareTo(nowValue)>=0){
            return n.length() - nowValue.length();
        }
        return n.length() - nowValue.length()-1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值