leetcode刷题第23天——1414,78,90

本文探讨了如何用贪心和回溯算法解决两个问题:一是找到和为特定数值k所需的最少斐波那契数个数,二是生成不重复整数数组的所有子集。通过实例代码展示了如何利用这些方法解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二十三天

1414 和为K的最少斐波那契数字数目

给你数字k ,请你返回和为k的斐波那契数字的最少数目,其中,每个斐波那契数字都可以被使用多次。

斐波那契数字定义为:

  • F1 = 1
  • F2 = 1
  • Fn = Fn-1 + Fn-2 , 其中 n > 2 。

数据保证对于给定的 k ,一定能找到可行解。

方法 贪心

由于所给的k一定能够表示成几个斐波那契数的和,依据斐波那契数列的性质,我们假设
K=f1+f2+f3+...fn K=f_1+f_2+f_3+...f_n K=f1+f2+f3+...fn
是所求的最少的斐波那契数的和。并且f按照降序排列。现在考虑f应当取何值,才能让整体的数量最少。一种很容易考虑到的方法是让每个f都尽可能的靠近K,这样能够使得数量最少,为什么这是对的?

如果我们选取了某个f2,它的值低于我们贪心的f1,由斐波那契数列的性质可以直到,一定存在另外一个数f3,使得f1=f2+f3,这样一来,如果我们选取了f2,会造成总的数量增加1

class Solution {
     public int findMinFibonacciNumbers(int k) {
        int[] fibNum = new int[]{1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986,102334155,165580141,267914296,433494437,701408733,1134903170};
        int n = fibNum.length;
        int res = 0;
        while (k > 0){
            int index = 0;
            while (fibNum[index] <= k) index++;
            k -= fibNum[index - 1];
            res++;
        }
        return res;
    }
}

78 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

方法 回溯

回溯问题的基本解决思路,属于模板题。

class Solution {
    List<List<Integer>> res;
    ArrayList<Integer> subSet;

    public List<List<Integer>> subsets(int[] nums) {
        res = new ArrayList<>();
        subSet = new ArrayList<>();
        getSubSet(nums, 0);
        return res;
    }

    public void getSubSet(int[] nums, int depth){
        if (depth == nums.length){
            res.add(new ArrayList<>(subSet));
            return;
        }
        
        subSet.add(nums[depth]);//选取第depth位置的数
        getSubSet(nums, depth + 1);
        subSet.remove(subSet.size() - 1);//不选第depth位置的数
        getSubSet(nums, depth + 1);
    }
}

90 子集Ⅱ

给你一个整数数组nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

方法 哈希+回溯

定义一个哈希函数唯一确定一个子集中的元素(只和数量和值有关,和顺序无关),使用HashSet保存每一个唯一的子集的hash值,如果发现相同,则不加入到结果中去。

class Solution {
    List<List<Integer>> res;
    ArrayList<Integer> subSet;
    Set<Long> set;
    boolean f;

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        res = new ArrayList<>();
        subSet = new ArrayList<>();
        set = new HashSet<>();
        for (int i : nums) set.add((long)i);
        f = set.size() == nums.length;
        set.clear();
        getSubSet(nums, 0);
        return res;
    }
    
    public long getHash(ArrayList<Integer> arrayList){
        long res = 1;
        for (int i : arrayList){
            if (i == 0) res -= 1315;
            else if (i == 1) res *= 11564;
            else if (i == -1) res += -1577;
            else res *= (5 * i * i);
        }
        return res;
    }

    public void getSubSet(int[] nums, int depth){
        if (depth == nums.length){
            if (!set.contains(getHash(subSet)) || f){
                set.add(getHash(subSet));
                res.add(new ArrayList<>(subSet));
            }
            return;
        }

        subSet.add(nums[depth]);
        getSubSet(nums, depth + 1);
        subSet.remove(subSet.size() - 1);
        getSubSet(nums, depth + 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值