1、组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
package leetcode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TheSumOfCombination_39 {
public static void main(String[] args) {
TheSumOfCombination_39 res = new TheSumOfCombination_39();
int[] candidates = {2,4,5,6,7};
int target = 10;
List<List<Integer>> result = res.combinationSum(candidates, target);
System.out.println(result.toString());
}
List<List<Integer>> list1 = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<Integer> list2 = new ArrayList<>();
int len = candidates.length;
Arrays.sort(candidates);
dfc(candidates,0,len,list2,target);
return list1;
}
public void dfc(int[] candidates,int begin,int len,List<Integer> list2,int target) {
if(target == 0) {
list1.add(new ArrayList<>(list2));
return;
}
for(int i = begin; i < len; i++) {
if(target-candidates[i] < 0) {
break;
}
list2.add(candidates[i]);
dfc(candidates,i,len,list2,target-candidates[i]);
list2.remove(list2.size()-1);
}
}
}
2、组合总和2
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
package leetcode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class theSumOfCombination_40 {
public static void main(String[] args) {
theSumOfCombination_40 res = new theSumOfCombination_40();
int[] candidates = {2,5,2,1,2};
int target = 5;
List<List<Integer>> result = res.combinationSum(candidates, target);
System.out.println(result.toString());
}
List<List<Integer>> list1 = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<Integer> list2 = new ArrayList<>();
int len = candidates.length;
Arrays.sort(candidates);
dfc(candidates,0,len,list2,target);
return list1;
}
public void dfc(int[] candidates,int begin,int len,List<Integer> list2,int target) {
if(target == 0) {
if(!list1.contains(new ArrayList<>(list2))) {
list1.add(new ArrayList<>(list2));
}
return;
}
for(int i = begin; i < len; i++) {
if(target-candidates[i] < 0) {
break;
}
list2.add(candidates[i]);
dfc(candidates,i+1,len,list2,target-candidates[i]);
list2.remove(list2.size()-1);
}
}
}
3、缺失的第一个正数
给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0]
输出: 3
示例 2:
输入: [3,4,-1,1]
输出: 2
你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间
方法一:将数组视为哈希表
由于题目要求我们“只能使用常数级别的空间”,而要找的数一定在 [1, N + 1] 左闭右闭(这里 N 是数组的长度)这个区间里。因此,我们可以就把原始的数组当做哈希表来使用。事实上,哈希表其实本身也是一个数组;
我们要找的数就在 [1, N + 1] 里,最后 N + 1 这个元素我们不用找。因为在前面的 N 个元素都找不到的情况下,我们才返回 N + 1;
那么,我们可以采取这样的思路:就把 1 这个数放到下标为 0 的位置, 2 这个数放到下标为 1 的位置,按照这种思路整理一遍数组。然后我们再遍历一次数组,第 1个遇到的它的值不等于下标的那个数,就是我们要找的缺失的第一个正数。
这个思想就相当于我们自己编写哈希函数,这个哈希函数的规则特别简单,那就是数值为 i 的数映射到下标为 i - 1 的位置。
时间复杂度:O(N),这里 NN是数组的长度。
空间复杂度:O(1)。
public class Solution {
public int firstMissingPositive(int[] nums) {
int len = nums.length;
for (int i = 0; i < len; i++) {
while (nums[i] > 0 && nums[i] <= len && nums[nums[i] - 1] != nums[i]) {
// 满足在指定范围内、并且没有放在正确的位置上,才交换
// 例如:数值 3 应该放在索引 2 的位置上
swap(nums, nums[i] - 1, i);
}
}
// [1, -1, 3, 4]
for (int i = 0; i < len; i++) {
if (nums[i] != i + 1) {
return i + 1;
}
}
// 都正确则返回数组长度 + 1
return len + 1;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
方法一:时间复杂度:O(N)O(N)O(N),这里 NNN 表示数组的长度。第 1 次遍历了数组,第 2 次遍历了区间 [1, len] 里的元素。
空间复杂度:O(N)O(N)O(N),把 NNN 个数存在哈希表里面,使用了 N 个空间。
class Solution {
public int firstMissingPositive(int[] nums) {
Set<Integer> list = new HashSet<>();
list.add(0);
for(int i : nums) {
if(i>0) {
list.add(i);
}
}
Integer[] positiveNums = list.toArray(new Integer[list.size()]);
Arrays.sort(positiveNums);
int m = 0;
for(int i = 1; i < positiveNums.length; i++) {
if(positiveNums[i] == i) {
}else {
return i;
}
m = i;
}
if(m == positiveNums.length-1) {
return ++m;
}
return -1;
}
}
4、接雨水
求每一列的水,我们只需要关注当前列,以及左边最高的墙,右边最高的墙就够了。
装水的多少,当然根据木桶效应,我们只需要看左边最高的墙和右边最高的墙中较矮的一个就够了。
所以,根据较矮的那个墙和当前列的墙的高度可以分为三种情况。
较矮的墙的高度大于当前列的墙的高度:
很明显,较矮的一边,也就是左边的墙的高度,减去当前列的高度就可以了,也就是 2 - 1 = 1,可以存一个单位的水。
较矮的墙的高度小于当前列的墙的高度:
正在求的列不会有水,因为它大于了两边较矮的墙。
-
较矮的墙的高度等于当前列的墙的高度。
和上一种情况是一样的,不会有水。
首先用两个数组,max_left [i] 代表第 i 列左边最高的墙的高度,max_right[i] 代表第 i 列右边最高的墙的高度。(一定要注意下,第 i 列左(右)边最高的墙,是不包括自身的)
对于 max_left我们其实可以这样求。
max_left [i] = Max(max_left [i-1],height[i-1])。它前边的墙的左边的最高高度和它前边的墙的高度选一个较大的,就是当前列左边最高的墙了。
对于 max_right我们可以这样求。
max_right[i] = Max(max_right[i+1],height[i+1]) 。它后边的墙的右边的最高高度和它后边的墙的高度选一个较大的,就是当前列右边最高的墙了。
这样,我们再利用解法二的算法,就不用在 for 循环里每次重新遍历一次求 max_left 和 max_right 了。
class Solution {
public int trap(int[] height) {
int[] max_left = new int[height.length];
int[] max_right = new int[height.length];
int sum = 0;
for(int i = 1;i < height.length - 1;i++){
max_left[i] = Math.max(max_left[i -1],height[i - 1]);
}
for(int i = height.length - 2;i > 0;i--){
max_right[i] = Math.max(max_right[i + 1],height[i + 1]);
}
for(int i = 1; i < height.length - 1; i++){
int min = Math.min(max_left[i] , max_right[i]);
if(height[i] < min){
sum += (min - height[i]);
}
}
return sum;
}
}
时间复杂度:O(n)。
空间复杂度:O(n),用来保存每一列左边最高的墙和右边最高的墙。
5、跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:
假设你总是可以到达数组的最后一个位置。我们知道最终要到达最后一个位置,然后我们找前一个位置,遍历数组,找到能到达它的位置,离它最远的就是要找的位置。然后继续找上上个位置,最后到了第 0 个位置就结束了。
至于离它最远的位置,其实我们从左到右遍历数组,第一个满足的位置就是我们要找的。
时间复杂度:O(n²),因为最坏的情况比如 1111111 ,position 会从 5 更新到 0,并且每次更新都会经历一个 for 循环。
空间复杂度:O(1)。
class Solution {
public int jump(int[] nums) {
int step = 0;
int position = nums.length - 1;
while(position != 0){
for(int i = 0; i < nums.length - 1 ;i++){
if(nums[i] >= position - i){
step++;
position = i;
break;
}
}
}
return step;
}
}
判断你是否能够到达最后一个位置。
解题思路:
如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点。
可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离 不断更新。
如果可以一直跳到最后,就成功了。
package leetcode; public class jump_55 { public static void main(String[] args) { int[] nums = {3,2,1,0,4}; System.out.println(canJump(nums)); } public static boolean canJump(int[] nums) { int k = 0; for (int i = 0; i < nums.length; i++) { if (i > k) return false; k = Math.max(k, i + nums[i]); } return true; } }