leetcode-100题
第二周(5.31~6.6)
34. 在排序数组中查找元素的第一个和最后一个位置
public class Solution {
//二分查找
public int[] searchRange(int[] nums, int target) {
if (nums.length == 0) {
return new int[]{-1, -1};
}
int firstPosition = findFirstPosition(nums, target);
if (firstPosition == -1) {
return new int[]{-1, -1};
}
int lastPosition = findLastPosition(nums, target);
return new int[]{firstPosition, lastPosition};
}
private int findFirstPosition(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
// ① 不可以直接返回,应该继续向左边找,即 [left, mid - 1] 区间里找
right = mid - 1;
} else if (nums[mid] < target) {
// 应该继续向右边找,即 [mid + 1, right] 区间里找
left = mid + 1;
} else {
// 此时 nums[mid] > target,应该继续向左边找,即 [left, mid - 1] 区间里找
right = mid - 1;
}
}
// 此时 left 和 right 的位置关系是 [right, left],注意上面的 ①,此时 left 才是第 1 次元素出现的位置
// 因此还需要特别做一次判断
if (left != nums.length && nums[left] == target) {
return left;
}
return -1;
}
private int findLastPosition(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
// 只有这里不一样:不可以直接返回,应该继续向右边找,即 [mid + 1, right] 区间里找
left = mid + 1;
} else if (nums[mid] < target) {
// 应该继续向右边找,即 [mid + 1, right] 区间里找
left = mid + 1;
} else {
// 此时 nums[mid] > target,应该继续向左边找,即 [left, mid - 1] 区间里找
right = mid - 1;
}
}
// 由于 findFirstPosition 方法可以返回是否找到,这里无需单独再做判断
return right;
}
}
39. 组合总和
画树状图
以输入candidate = [2, 3, 6, 7]
,target = 7
为例:
说明:
-
以
target = 7
为根节点,创建一个分支时 做减法。 -
每一个箭头表示:从父亲节点的数值减去边上的数值,得到孩子节点的数值。边的值就是题目中给出的
candidate
数组的每个元素值 -
减到0或负数时停止:结点0和负数为叶子结点
-
所有从根结点到结点0的路径(只能从上到下,没有回路)就是题目要找的一个结果
解决重复路径的难点:
产生重复的原因:
在节点展开分支做减法时,按照题目所说,每一个元素都可以重复使用,所以考虑了所有的候选数,因此出现了重复的列表。
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
public class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
int len = candidates.length;
List<List<Integer>> res = new ArrayList<>();
if (len == 0) {
return res;
}
Deque<Integer> path = new ArrayDeque<>();
dfs(candidates, 0, len, target, path, res);
return res;
}
/**
* @param candidates 候选数组
* @param begin 搜索起点
* @param len 冗余变量,是 candidates 里的属性,可以不传
* @param target 每减去一个元素,目标值变小
* @param path 从根结点到叶子结点的路径,是一个栈
* @param res 结果集列表
*/
private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res) {
// target 为负数和 0 的时候不再产生新的孩子结点
if (target < 0) {
return;
}
if (target == 0) {
res.add(new ArrayList<>(path));
return;
}
// 重点理解这里从 begin 开始搜索的语意
for (int i = begin; i < len; i++) {
path.addLast(candidates[i]);
// 注意:由于每一个元素可以重复使用,下一轮搜索的起点依然是 i,这里非常容易弄错
dfs(candidates, i, len, target - candidates[i], path, res);
// 状态重置
path.removeLast();
}
}
}
class Solution {
// 回溯算法
// https://leetcode-cn.com/problems/combination-sum/solution/zu-he-zong-he-hui-su-suan-fa-jian-zhi-me-ed02/
public List<List<Integer>> combinationSum(int[] candidates, int target) {
//背包问题
//可重复,就是完全背包问题
//不考虑顺序,外层为nums,内层为target
List<List<Integer>> res = new ArrayList<List<Integer>>();
LinkedList<Integer> inner = new LinkedList<>();
Arrays.sort(candidates);
backtrace(inner,candidates,target,0,res);
return res;
}
private void backtrace(LinkedList<Integer> inner,int[] candidates,int target,int index,List<List<Integer>> res){
if(target < 0){
return;
}
if(target == 0){
res.add(new ArrayList<>(inner));
return;
}
for(int i = index;i < candidates.length;i++){
if(candidates[i] > target) return;
if(i > 0 && candidates[i] == candidates[i-1]) continue;
inner.addLast(candidates[i]);
backtrace(inner,candidates,target - candidates[i],i,res);
inner.removeLast();
}
}
}
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
List<List<Integer>>lists = new ArrayList<>();
if(len == 0 || nums == null) return lists;
Quan(len, 0, lists, nums);
return lists;
}
//全排列四个参数
public void Quan(int len, int index, List<List<Integer>>lists, int[]nums){
if(index == len){
List<Integer>list = new ArrayList<>();
for(int num : nums){
list.add(num);
}
lists.add(list);
}
else{
for(int i = index; i<len; i++){
swap(nums, index, i);
Quan(len, index+1, lists, nums);
swap(nums, index, i);
}
}
}
public void swap(int[]nums, int i, int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
public class Solution {
public List<List<Integer>> permute(int[] nums) {
// 首先是特判
int len = nums.length;
// 使用一个动态数组保存所有可能的全排列
List<List<Integer>> res = new ArrayList<>();
if (len == 0) {
return res;
}
boolean[] used = new boolean[len];
List<Integer> path = new ArrayList<>();
dfs(nums, len, 0, path, used, res);
return res;
}
private void dfs(int[] nums, int len, int depth,
List<Integer> path, boolean[] used,
List<List<Integer>> res) {
if (depth == len) {
// 3、不用拷贝,因为每一层传递下来的 path 变量都是新建的
res.add(path);
return;
}
for (int i = 0; i < len; i++) {
if (!used[i]) {
// 1、每一次尝试都创建新的变量表示当前的"状态"
List<Integer> newPath = new ArrayList<>(path);
newPath.add(nums[i]);
boolean[] newUsed = new boolean[len];
System.arraycopy(used, 0, newUsed, 0, len);
newUsed[i] = true;
dfs(nums, len, depth + 1, newPath, newUsed, res);
// 2、无需回溯
}
}
}
}
//暴力翻转
class Solution {
public void rotate(int[][] matrix) {
int n=matrix.length;
int[][] res=new int [n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
res[j][n-i-1]=matrix[i][j];
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
matrix[i][j]=res[i][j];
}
}
}
}
class Solution {
public void rotate(int[][] matrix) {
int add = 0;
int temp = 0;
int pos1 = 0;
int pos2 = matrix[0].length - 1;
while (pos1 < pos2){
add = 0;
while (add < pos2 - pos1){
temp = matrix[pos1][pos1 + add];
matrix[pos1][pos1 + add] = matrix[pos2 - add][pos1];
matrix[pos2 - add][pos1] = matrix[pos2][pos2 -add];
matrix[pos2][pos2 -add] = matrix[pos1 + add][pos2];
matrix[pos1 + add][pos2] = temp;
add++;
}
pos1++;
pos2--;
}
}
}
//https://leetcode-cn.com/problems/rotate-image/solution/li-kou-48xiao-bai-du-neng-kan-dong-de-fang-fa-zhu-/
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> result=new ArrayList<>();
//分别对应26个英文字母;
int[] primes = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89,97,101};
Map<Long, List<String>> map = new HashMap<>();
for (String s : strs) {
long p = 1;
char[] arr = s.toCharArray();
for (char a : arr) {
//然后每个字符串的字符减去 ' a ' ,然后取到 prime 中对应的质数。把它们累乘。
p *= primes[a - 'a'];
}
if(!map.containsKey(p)){
List<String> t=new ArrayList<>();
map.put(p,t);
result.add(t);
}
map.get(p).add(s);
}
return result;
}
}
//https://leetcode-cn.com/problems/group-anagrams/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by--16/
//直接暴力破解
class Solution {
public int maxSubArray(int[] nums) {
int ans = nums[0], sum = 0;
for(int num: nums) {
if(sum > 0) {
sum += num;
} else {
sum = num;
}
ans = Math.max(ans, sum);
}
return ans;
}
}
//https://leetcode-cn.com/problems/maximum-subarray/solution/hua-jie-suan-fa-53-zui-da-zi-xu-he-by-guanpengchn/