leetcode306. 累加数(回溯算法-java)

265 篇文章 2 订阅
235 篇文章 0 订阅

leetcode306. 累加数

累加数 是一个字符串,组成它的数字可以形成累加序列。
一个有效的 累加序列 必须 至少 包含 3 个数。除了最开始的两个数以外,序列中
每个后续数字必须是它之前两个数字之和。
给你一个只包含数字 ‘0’-‘9’ 的字符串,编写一个算法来判断给定输入是否是 累加数 。如果是,返回 true ;否则,返回 false 。
说明:累加序列里的数,除数字 0 之外,不会 以 0 开头,所以不会出现 1, 2, 03 或者 1, 02, 3 的情况。

示例 1:
输入:“112358”
输出:true
解释:累加序列为: 1, 1, 2, 3, 5, 8 。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8

示例 2:
输入:“199100199”
输出:true
解释:累加序列为: 1, 99, 100, 199。1 + 99 = 100, 99 + 100 = 199

提示:
1 <= num.length <= 35
num 仅由数字(0 - 9)组成

进阶:你计划如何处理由过大的整数输入导致的溢出?

回溯算法

给定的 nums的长度只有 35,且要求序列的第三个数开始由前两个数相加而来。
容易想到通过 DFS 爆搜每个数的分割点,同时利用累加数的特性(第三个数起,每个数均由为前两数之和)进行剪枝。

具体的,我们可以实现一个 boolean dfs(int u) 函数,入参为当前决策到 num 的哪一位,返回值为决策结果(序列)是否为累加数序列,爆搜过程中的分割数序列存放到 list 中。

由于是 从位置 index 作为开始位置决策如何分割出当前数 x,我们可以枚举当前数的结束位置,范围为 [u,n−1],但需要注意分割数不能包含前导零,即如果 num[u]=0,则当前数只能为 0。

同时,一个合法的分割数必然满足「其值大小为前两数之和」,因此当前数 x 能够被添加到 list 的充要条件为:

  1. list 长度不足 2,即 x 为序列中的前两数,不存在值大小的约束问题,x 可以被直接到 listlistlist 并继续爆搜;
  1. list 长度大于等于 2,即 x 需要满足「其值大小为前两数之和」要求,以此条件作为剪枝,满足要求的 x 才能追加到 list 中并继续爆搜。

最后,在整个 DFS 过程中我们需要监测「当前数」与「前两数之和」是否相等,而分割数长度最大为 35,存在溢出风险,我们需要实现「高精度加法」,实现一个 check 函数,用于检查 a + b 是否为 c,其中 a、b 和 c 均为使用「逆序」存储数值的数组(最高位对应个位,举个 🌰,a=35,则有 [5, 3])。

若爆搜过程能顺利结束(得到长度至少为 3的序列),则说明能够拆分出累加数序列,返回 True,否则返回 False。
?至此,我们解决了本题,并通过引入「高精度」来回答了「进阶」部分的问题。

代码演示

class Solution {
    String _num;
    int n;
    List<List<Integer>> lists = new ArrayList<>();
    public boolean isAdditiveNumber(String num) {
        if (num.length() < 3){
            return false;
        }
        _num = num;
        n = num.length();
        
        return dfs(0);
    }

    /**
     * 回溯算法
     * @param index
     * @return
     */
    boolean dfs(int index){
        int m = lists.size();
        if (index == n){
            return m >= 3;
        }
        //如果当前数字是0,那么0 只能当作一个单独数字,不能和别的进行组合,
        //所以max = index + 1;后续选择时,只能选到index
        //不是0 的话,可以一直向后选,直到最后一个位置.
        int max = _num.charAt(index) == '0' ? index + 1 : n;
        ArrayList<Integer> cur = new ArrayList<>();
        for (int i = index;i < max;i++){
            //将数字逆序加进来,例如 199  -> 加进来 变为 991
            //为了解决数字溢出的问题.
            cur.add(0,_num.charAt(i) - '0');
            //判断加进来的标准
            if (m < 2 || check(lists.get(m - 2),lists.get(m - 1),cur)){
                lists.add(cur);
                if (dfs(i + 1)){
                    return true;
                }
                lists.remove(lists.size() - 1);
            }
        }
        return false;
    }

    /**
     * 判断当前数字是否是前两个数字之和
     * @param a
     * @param b
     * @param c
     * @return
     */
    public boolean check(List<Integer> a,List<Integer>b,List<Integer>c){
        ArrayList<Integer> ans = new ArrayList<>();
        int t = 0;
        for (int i = 0;i < a.size() || i < b.size();i++){
            if (i < a.size()){
                t += a.get(i);
            }
            if (i < b.size()){
                t += b.get(i);
            }
            
            ans.add(t % 10);
            t /= 10;
        }
         if (t > 0){
            ans.add(t);
        }
        if (ans.size() != c.size()){
            return false;
        }
        for (int i = 0 ; i < c.size();i++){
            if (c.get(i) != ans.get(i)){
                return false;
            }
        }
        return true;
    }
}

回溯算法

leetcode698. 划分为k个相等的子集

leetcode93. 复原 IP 地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用Java中的排序功能来实现。可以使用Arrays.sort()函,将列表中的元素按照字母顺序排序,或者使用Collections.sort()函,将列表中的元素按用户指定的排序规则排序。 ### 回答2: 为了实现LeetCode 2561题(Rearranging Fruits)的要求,需要使用Java编程语言。主要思路是遍历给定的水果组,同时用一个哈希表来记录每个水果出现的次。然后根据题目要求,重新排列水果使得相同类型的水果尽可能接近,并且按照出现次的非递增顺序排序。 具体实现步骤如下: 1. 创建一个HashMap来存储每个水果的出现次。遍历给定的水果组,如果该水果已经存在于HashMap中,则将其出现次加1;否则,将该水果添加到HashMap,并将其出现次初始化为1。 2. 创建一个ArrayList来存储已经排列好的水果。通过HashMap的entrySet方法获取到每种水果和它的出现次,然后将这些entry按照出现次的非递增顺序进行排序。 3. 遍历排序好的entry集合,根据每个水果的出现次,在ArrayList中连续添加相应量的水果。 4. 返回排列好的水果组。 以下是Java代码的示例实现: ```java import java.util.*; class Solution { public String[] rearrange(String[] fruits) { HashMap<String, Integer> fruitCountMap = new HashMap<>(); // 统计每个水果的出现次 for (String fruit : fruits) { if (fruitCountMap.containsKey(fruit)) { fruitCountMap.put(fruit, fruitCountMap.get(fruit) + 1); } else { fruitCountMap.put(fruit, 1); } } ArrayList<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(fruitCountMap.entrySet()); // 根据出现次进行非递增排序 Collections.sort(sortedEntries, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> entry1, Map.Entry<String, Integer> entry2) { return entry2.getValue().compareTo(entry1.getValue()); } }); ArrayList<String> rearrangedFruits = new ArrayList<>(); // 根据出现次连续添加水果 for (Map.Entry<String, Integer> entry : sortedEntries) { String fruit = entry.getKey(); int count = entry.getValue(); for (int i = 0; i < count; i++) { rearrangedFruits.add(fruit); } } return rearrangedFruits.toArray(new String[0]); } } ``` 使用以上代码,可以对给定的水果组进行重新排列,使得相同类型的水果尽可能接近,并且按照出现次的非递增顺序进行排序。返回的结果就是排列好的水果组。 ### 回答3: 题目要求将一个字符串中的水果按照特定规则重新排列。我们可以使用Java来实现这个问题。 首先,我们需要定义一个函来解决这个问题。 ```java public static String rearrangeFruits(String fruits) { // 将字符串转换为字符组方便处理 char[] fruitArray = fruits.toCharArray(); // 统计每种水果的量 int[] fruitCount = new int[26]; for (char fruit : fruitArray) { fruitCount[fruit - 'a']++; } // 创建一个新的字符组来存储重新排列后的结果 char[] rearrangedFruitArray = new char[fruitArray.length]; // 逐个将水果按照规则放入新组中 int index = 0; for (int i = 0; i < 26; i++) { while (fruitCount[i] > 0) { rearrangedFruitArray[index++] = (char) ('a' + i); fruitCount[i]--; } } // 将字符组转换为字符串并返回 return new String(rearrangedFruitArray); } ``` 上述代码中,我们首先将字符串转换为字符组,并使用一个长度为26的组来统计每一种水果的量。然后,我们创建一个新的字符组来存储重新排列后的结果。 接下来,我们利用双重循环将每一种水果按照规则放入新组中。最后,我们将字符组转换为字符串并返回。 例如,如果输入字符串为`"acbba"`,则经过重新排列后,输出结果为`"aabbc"`。 这样,我们就用Java实现了题目要求的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值