LeetCode 313. 超级丑数(最小堆、动态规划)/413. 等差数列划分/167. 两数之和 II - 输入有序数组

313. 超级丑数

2021.8.9 每日一题

题目描述

超级丑数 是一个正整数,并满足其所有质因数都出现在质数数组 primes 中。

给你一个整数 n 和一个整数数组 primes ,返回第 n 个 超级丑数 。

题目数据保证第 n 个 超级丑数 在 32-bit 带符号整数范围内。

示例 1:

输入:n = 12, primes = [2,7,13,19]
输出:32
解释:给定长度为 4 的质数数组 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。

示例 2:

输入:n = 1, primes = [2,3,5]
输出:1
解释:1 不含质因数,因此它的所有质因数都在质数数组 primes = [2,3,5] 中。

提示:

1 <= n <= 106
1 <= primes.length <= 100
2 <= primes[i] <= 1000
题目数据 保证 primes[i] 是一个质数
primes 中的所有值都 互不相同 ,且按 递增顺序 排列

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/super-ugly-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

做过丑数一二,万万没想到还有个超级丑数

堆的做法,每次从最小堆堆顶取出最小数,然后和给定的表中元素相乘,得到新的超级丑数
注意得用long,因为堆中有的数可能会越界

class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
        //昨晚看了一下,应该是动规
        //所有质因数都出现在数组里,应该是用数组中的数动规
        //刚开始数组中总共l+1个数,然后就是里面的数互相乘,得到下一个数
        //其实和丑数2差不多,那应该怎么做呢
        //dp[i][j]
        //首先写个堆把,感觉堆比较好想

        int l = primes.length;
        PriorityQueue<Long> pq = new PriorityQueue<>();

        //每次弹出堆顶的数,然后和primes中的数相乘,如果没有存在过,就加入堆

        //用一个哈希表去重,防止重复
        Set<Long> set = new HashSet<>();
        set.add(1L);
        pq.offer(1L);
        for(int i = 1; i < n; i++){
            //当前堆顶的数
            long temp = pq.poll();
            for(int base : primes){
                long t = temp * base;
                if(!set.contains(t)){
                    pq.offer(t);
                    set.add(t);
                }
            }
        } 
        long res = pq.poll();
        return (int)res;
    }
}

动态规划
最后丑数序列中的数,都是每个primes中的数(加上1),不断相乘得到的
所以可以想到,用一个base数组,对应primes数组。例如,base[j]表示当前位置的质因数primes[j]可以乘的最小数
但是如果每次求这个数组中的最小数的话,肯定会超时
所以用一个dp数组,记录当前计算到的丑数
然后base中存放的是,可以乘的最小数在dp数组中对应的下标

再更新的时候,每次计算base数组中每一个位置对应的数dp[base[j]] * primes[j],得到当前质因数能计算得到的最小值,然后所有位置取最小值,就可以得到第i个丑数,将这个丑数放进dp数组中
同时注意:因为比如第一个例子中的,26这个数字,可以由2*13和13*2得到,所以要更新两个base中的位置

ps:10^6*100 我还以为会超时,结果能超几乎100。。

class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
        //昨晚看了一下,应该是动规
        //所有质因数都出现在数组里,应该是用数组中的数动规
        //刚开始数组中总共l+1个数,然后就是里面的数互相乘,得到下一个数
        //其实和丑数2差不多,那应该怎么做呢
        //dp[i][j]

        int l = primes.length;
        
        //想想动规,每个数有l个数可以乘
        //然后标记哪个数被乘过了,如果都被乘过了,那么就下一个数
        //但是怎么取下一个数呢
        //对于第一个例子,比如第一个数是1,那么它有l个数可以乘
        //从小到大,得到第二个数是2,dp[1] = 1; dp[2] = 2;
        //然后比较1*7和2 *2 ,得到第三个数是4
        //然后比较1*7和4*2
        //有了,就是对于primes中的每个数,都有一个当前的最小数,然后根据这个最小数,往后计算

        //对应primes中每个位置的最小数
        int[] base = new int[l];
        //刚开始所有的最小数都为1
        Arrays.fill(base, 1);

        //记录当前丑数
        int[] dp = new int[n + 1];
        dp[1] = 1;
        for(int i = 2; i <= n; i++){
            //计算当前的超级丑数
            int res = Integer.MAX_VALUE;
            for(int j = 0; j < l; j++){
                if(dp[base[j]] * primes[j] < res){
                    res = dp[base[j]] * primes[j];
                }
            }
            //如果当前最小数是t位置取的,那么就把t位置的下标往后移一位
            //但是因为会有重复的,所以需要判断
            for(int j = 0; j < l; j++){
                if(dp[base[j]] * primes[j] == res){
                    base[j]++;
                }
            }
            dp[i] = res;
            //System.out.println(res);
        }
        return dp[n];
    }
}

413. 等差数列划分

2021.8.10 每日一题

题目描述

如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。

例如,[1,3,5,7,9]、[7,7,7,7] 和 [3,-1,-5,-9] 都是等差数列。
给你一个整数数组 nums ,返回数组 nums 中所有为等差数组的 子数组 个数。

子数组 是数组中的一个连续序列。

示例 1:

输入:nums = [1,2,3,4]
输出:3
解释:nums 中有三个子等差数组:[1, 2, 3]、[2, 3, 4] 和 [1,2,3,4] 自身。

示例 2:

输入:nums = [1]
输出:0

提示:

1 <= nums.length <= 5000
-1000 <= nums[i] <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/arithmetic-slices
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

判断之间的差值相等就OK
然后统计这个相等的次数,直到不相等时,得到了这个等差序列最长的长度。例如 1 2 3 4 5,得到了count = 3
因为这只是记录了1开头的等差数列的个数
当2开头,这个个数是2,;3开头是1;因此可以得到,当找到不相等时,可以通过(1+count)*count/2来求得这个序列的等差数列的个数,加到结果集中

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        //记录每两个数之间的差值
        int l = nums.length;
        if(l < 3)
            return 0;
        int[] diff = new int[l - 1];
        for(int i = 1; i < l; i++){
            diff[i - 1] = nums[i] - nums[i - 1];
        }
        
        int count = 0;
        int res = 0;
        int pre = diff[0];
        for(int i = 1; i < l - 1; i++){
            if(diff[i] == pre){
                count++;
            }else{
                count = (count + 1) * count / 2;
                res += count;
                pre = diff[i];
                count = 0;
            }
        }
        if(count != 0){
            count = (count + 1) * count / 2;
            res += count;
        }

        return res;
    }
}

167. 两数之和 II - 输入有序数组

题目描述

给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。

你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例 1:

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

示例 2:

输入:numbers = [2,3,4], target = 6
输出:[1,3]

示例 3:

输入:numbers = [-1,0], target = -1
输出:[1,2]

提示:

2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers 按 递增顺序 排列
-1000 <= target <= 1000
仅存在一个有效答案

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

手贱,点开了一个学习的打开,又不想错过那个徽章,所以能做的话来做一下

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int l = numbers.length;
        int left = 0;
        int right = l - 1;
        while(left < right){
            while(numbers[left] + numbers[right] < target)
                left++;
            while(numbers[left] + numbers[right] > target)
                right--;
            if(numbers[left] + numbers[right] == target)
                break;
        }
        return new int[]{left + 1, right + 1};
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值