213.打家劫舍2
该题目和之前的打家劫舍有点类似,不过该题目是一个的家庭是环形(代表第一位和最后一位不能够同时盗窃)。
讲这道题我们可以将环形进行拆分,既然首位不发同时获取到,那么我们不如将这个环拆分成两个单列表(一个不包含第一家,一个不包含最后一家),根据这个情况的话我们可以将两个单列进行两次打家劫舍,取最大值即可。
注意:有两种特殊情况需要进行考虑一下,长度 = 0 / = 1,因为无法数组无法进行割舍。
public int rob(int[] nums) {
if(nums.length == 0) {
return 0;
}
if(nums.length == 1) {
return nums[0];
}
int num1 = myRob(Arrays.copyOfRange(nums,0,nums.length - 1));
int num2 = myRob(Arrays.copyOfRange(nums,1,nums.length));
return Math.max(num1,num2);
}
public int myRob(int[] nums) {
int[] dp = new int[nums.length + 1];
int ans = nums[0];
//从1开始偷
dp[1] = nums[0];
for(int i=2; i<dp.length; i++) {
dp[i] = Math.max(dp[i - 1],dp[i - 2] + nums[i - 1]);
ans = Math.max(ans,dp[i]);
}
return ans;
}
215.数组中的第K个最大元素(Top K问题)
我们可以使用快排的分区思想,排序后左右都是局部有序,[小/大 < 基准值 < 大/小] 这样的结构。
所以我们可以轻易的判断出基准值所处在的位置(即前面有多少个大于自身的数,根据这个数目选择指针移动的方向)
//寻找第K大数(代表前面有k个数)
public int findKthLargest(int[] nums, int k) {
return partition(nums,0,nums.length - 1,k);
}
//使用快排的思想(从大到小排序奥)
public int partition(int[] nums, int left, int right, int k) {
if(left > right) {
return -1;
}
int l = left;
int r = right;
int target = nums[left];
while(l < r) {
while(l < r && nums[r] <= target) r--;
while(l < r && nums[l] >= target) l++;
if(l < r) {
int temp = nums[l];
nums[l] = nums[r];
nums[r] = temp;
}
}
//将比较值放在合适位置
int temp = nums[l];
nums[l] = nums[left];
nums[left] = temp;
//比target大的有多少个
int before = l - left;
if(before == k - 1) {
return nums[l];
}else if(before > k - 1) { //第K大还在更前面
return partition(nums,left,l - 1,k);
}else {
return partition(nums,l + 1,right,k - before - 1);
}
}
还有一种堆排序的方法,创建一个小顶堆(堆顶为堆内最小值),该堆维护一个size = k,则整个数组遍历结束后堆顶即为topK。
//寻找第K大数(代表前面有k个数)
public int findKthLargest(int[] nums, int k) {
//最小堆
Queue<Integer> queue = new PriorityQueue<>();
for(int i=0; i<nums.length; i++) {
if(queue.size() < k) {
queue.offer(nums[i]);
}else {
//此时堆顶为堆内最小值
if(queue.peek() < nums[i]) {
queue.remove();
queue.offer(nums[i]);
}
}
}
return queue.remove();
}
216.组合总和3
也是回溯算法,直接上代码了,没有什么难度。
public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> lists = new ArrayList<>();
dfs(lists,new ArrayList<>(),k,n,1);
return lists;
}
public void dfs(List<List<Integer>> lists, List<Integer> list, int k, int n, int num) {
if(k == 0 && n == 0) {
lists.add(new ArrayList<>(list));
return;
}
if(k <= 0) {
return;
}
for(int i=num; i<=9; i++) {
if(i > n) {
break;
}
list.add(i);
dfs(lists,list,k - 1,n - i, i + 1);
list.remove(list.size() - 1);
}
}
217.存在重复元素
先排序直接在判断和前一位的关系即可。
//判断是否存在重复元素
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
for(int i=1; i<nums.length; i++) {
if(nums[i - 1] == nums[i]) {
return true;
}
}
return false;
}
219.存在重复元素2
该题目的要求就是在两个值相同的前提下,使得两个下标差的绝对值 <= k。
我们可以通过记录一个数对应的下标,如果出现重复时,则此时我们拥有当前下标和上一次出现该元素的下标,判断一下是否符合。
如果不符合要求的话,则更新map中保存的元素对应的下标,用于下次一重复进行判断。
public boolean containsNearbyDuplicate(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
for(int i=0; i<nums.length; i++) {
if(map.containsKey(nums[i])) {
int index = map.get(nums[i]);
if(i - index <= k) {
return true;
}
map.put(nums[i],i);
}else {
map.put(nums[i],i);
}
}
return false;
}