【code基础】剪枝操作
剪枝操作可以去除重复无意义的操作,从而提升时间复杂度和空间复杂度
剪枝的常见思路:
- 排序和去重,这里常见
Arrays.sort(nums)
HashSet<>()
- 提前结束循环或者跳出循环
以力扣上的2344题为例: https://leetcode.cn/problems/minimum-deletions-to-make-array-divisible/
给你两个正整数数组 nums 和 numsDivide 。你可以从 nums 中删除任意数目的元素。
请你返回使 nums 中 最小 元素可以整除 numsDivide 中所有元素的 最少 删除次数。如果无法得到这样的元素,返回 -1 。
如果 y % x == 0 ,那么我们说整数 x 整除 y 。
输入:nums = [2,3,2,4,3], numsDivide = [9,6,9,3,15]
输出:2
解释:
[2,3,2,4,3] 中最小元素是 2 ,它无法整除 numsDivide 中所有元素。
我们从 nums 中删除 2 个大小为 2 的元素,得到 nums = [3,4,3] 。
[3,4,3] 中最小元素为 3 ,它可以整除 numsDivide 中所有元素。
可以证明 2 是最少删除次数。
输入:nums = [4,3,6], numsDivide = [8,2,6,10]
输出:-1
解释:
我们想 nums 中的最小元素可以整除 numsDivide 中的所有元素。
没有任何办法可以达到这一目的。
总结思路:其实是求numsDivide 的最小公约数
- 对nums数组进行排序,最小公约数,那就是从小到大
- numsDivide数组中可能会出现重复元素,如果使用暴力遍历,会产生时间的浪费,这里先去重,并且取最小值,为nums数组遍历的上限(超过该最小值也不是最小公约数)
- nums数组中也存在重复的元素,这里使用set集合,如果是重复元素进行拦截,计数器直接+1,就不进入复杂的计算过程了
//超出时间限制,增加剪枝操作
public int minOperations(int[] nums, int[] numsDivide) {
//1.对numsDivide做剪枝操作,使用TreeSet进行排序
TreeSet<Integer> set = new TreeSet<>();
for (int i = 0; i < numsDivide.length; i++) {
set.add(numsDivide[i]);
}
int min = set.first();
//2.因为要对nums数组统计删除的个数,所以这里是对nums数组的遍历,
// 增加了排序,剪枝 nums[i] <= min以及使用HashSet去重
Arrays.sort(nums);
HashSet<Integer> s = new HashSet<>();
for (int i = 0; i<nums.length && nums[i] <= min;) {
if (!s.add(nums[i])) i++;
else if (canDivide(nums[i],set)) return i;
else i++;
}
return -1;
}
public boolean canDivide(int num,TreeSet<Integer> set){
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()){
if ((iterator.next())%num !=0) return false;
}
return true;
}