leecode更新

This commit is contained in:
markilue 2023-03-08 21:47:45 +08:00
parent 6eafee1860
commit 1e6629760f
5 changed files with 602 additions and 0 deletions

View File

@ -0,0 +1,93 @@
package com.markilue.leecode.hot100;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-08 09:56
*@Description:
* TODO 力扣64题 最小路径和:
* 给定一个包含非负整数的 m x n 网格 grid 请找出一条从左上角到右下角的路径使得路径上的数字总和为最小
* 说明每次只能向下或者向右移动一步
*@Version: 1.0
*/
public class T31_MinPathSum {
/**
* 动态规划法:
* TODO DP五部曲:
* 1.dp定义: dp[i][j]表示到达i,j的最小路径和
* 2.dp状态转移方程:
* dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j]
* 3.dp初始化: dp[0][0]=grid[0][0];
* 4.dp遍历顺序:
* 5.dp举例推导:
* 速度击败94.76% 内存击败47.9% 2ms
* @param grid
* @return
*/
public int minPathSum(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
for (int i = 1; i < m; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int i = 1; i < n; i++) {
dp[0][i] = dp[0][i - 1] + grid[0][i];
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
int[][] memo;
/**
* 官方最快:回溯+记忆化搜索
* 速度击败100% 内存击败19.31% 0ms
* @param grid
* @return
*/
public int minPathSum1(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
memo = new int[m][n];
for (int[] row : memo)
Arrays.fill(row, -1);
return dp(grid,m-1,n-1);
}
int dp(int[][] grid,int i,int j){
if(i == 0 && j == 0){
return grid[0][0];
}
if(i <0 || j<0){
return Integer.MAX_VALUE;
}
if(memo[i][j] != -1){
return memo[i][j];
}else{
memo[i][j] = Math.min(dp(grid,i-1,j),dp(grid,i,j-1)) + grid[i][j];
}
return memo[i][j];
}
}

View File

@ -0,0 +1,57 @@
package com.markilue.leecode.hot100;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-08 10:13
*@Description:
* TODO 力扣70题 爬楼梯:
* 假设你正在爬楼梯需要 n 阶你才能到达楼顶
* 每次你可以爬 1 2 个台阶你有多少种不同的方法可以爬到楼顶呢
*@Version: 1.0
*/
public class T32_ClimbStairs {
/**
* 思路:缩维dp当前位置可以从上一个位置和上两个位置爬到
* 速度击败100% 内存击败43.59%
* @param n
* @return
*/
public int climbStairs(int n) {
if (n < 2) return 1;
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i < dp.length; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
/**
* 思路:通用动态规划法 ->爬几楼都行
* 速度击败100% 内存击败79.7%
* @param n
* @return
*/
public int climbStairs1(int n) {
if (n < 2) return 1;
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
int[] step={1,2};//可以爬一楼或者两楼
for (int i = 2; i < dp.length; i++) {
for (int j = 0; j < step.length; j++) {
dp[i] += dp[i - step[j]];
}
}
return dp[n];
}
}

View File

@ -0,0 +1,99 @@
package com.markilue.leecode.hot100;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-08 10:33
*@Description:
* TODO 力扣72题 编辑距离:
* 给你两个单词 word1 word2 请返回将 word1 转换成 word2 所使用的最少操作数
* 你可以对一个单词进行如下三种操作
* 插入一个字符
* 删除一个字符
* 替换一个字符
*@Version: 1.0
*/
public class T33_MinDistance {
/**
* 思路:
* TODO dp五部曲:
* 1.dp定义: dp[i][j]表示word1[0-i]变到word2[0-j]需要多少步
* 2.dp状态转移方程:
* 1.word1[i]==word1[j] 看看剩下的需要多少步
* dp[i][j]=dp[i-1][j-1]
* 2.word1[i]!=word1[j] 看看增加或者删除,修改能否到达
* dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
* 3.dp初始化:dp[0][j]=j;dp[i][0]=i
* 4.dp遍历顺序:
* 5.dp举例推导:
* 速度击败87.48% 内存击败12.47% 4ms
* @param word1
* @param word2
* @return
*/
public int minDistance(String word1, String word2) {
int length1 = word1.length();
int length2 = word2.length();
int[][] dp = new int[length1 + 1][length2 + 1];
for (int i = 0; i < dp.length; i++) {
dp[i][0] = i;
}
for (int i = 1; i < dp[0].length; i++) {
dp[0][i] = i;
}
for (int i = 1; i < dp.length; i++) {
for (int j = 1; j < dp[0].length; j++) {
if (word1.charAt(i-1) == word2.charAt(j-1)) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
}
return dp[length1][length2];
}
/**
* 官方最快:回溯+记忆化搜搜
* 速度击败100% 内存击败5.1% 2ms
*/
int[][] meno ;
public int minDistance1(String word1, String word2) {
meno = new int[word1.length()][word2.length()];
for (int[] ints : meno) {
Arrays.fill(ints,-1);
}
return dp(word1,word1.length()-1,word2,word2.length()-1);
}
private int dp(String word1, int i, String word2, int j) {
if(i==-1){
return j+1;
}
if(j==-1){
return i+1;
}
if(meno[i][j]!=-1){
return meno[i][j];
}
if(word1.charAt(i)==word2.charAt(j)){
meno[i][j] = dp(word1,i-1,word2,j-1);
}else{
meno[i][j] = Math.min(Math.min(dp(word1,i,word2,j-1),dp(word1,i-1,word2,j)),dp(word1,i-1,word2,j-1))+1;
}
return meno[i][j];
}
}

View File

@ -0,0 +1,102 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-08 10:48
*@Description:
* TODO 力扣75题 颜色分类:
* 给定一个包含红色白色和蓝色 n 个元素的数组 nums 原地对它们进行排序使得相同颜色的元素相邻并按照红色白色蓝色顺序排列
* 我们使用整数 0 1 2 分别表示红色白色和蓝色
* 必须在不使用库内置的 sort 函数的情况下解决这个问题
*@Version: 1.0
*/
public class T34_SortColors {
@Test
public void test(){
int[] nums ={1};
sortColors(nums);
System.out.println(Arrays.toString(nums));
}
/**
* 桶子法:三个桶分别装0 1 2
* 速度击败100% 内存击败43.28% 0ms
* @param nums
*/
public void sortColors(int[] nums) {
int[] zero = new int[nums.length];
int[] one = new int[nums.length];
int[] two = new int[nums.length];
int zeroIndex = 0;
int oneIndex = 0;
int twoIndex = 0;
//遍历nums,放进桶里
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
zero[zeroIndex++] = nums[i];
} else if (nums[i] == 1) {
one[oneIndex++] = nums[i];
} else {
two[twoIndex++] = nums[i];
}
}
int resultIndex = 0;
//把0放回来
for (int i = 0; i < zeroIndex; i++) {
nums[resultIndex++] = zero[i];
}
//把1放回来
for (int i = 0; i < oneIndex; i++) {
nums[resultIndex++] = one[i];
}
//把2放回来
for (int i = 0; i < twoIndex; i++) {
nums[resultIndex++] = two[i];
}
}
/**
* 官方题解双指针法:遇上0或者1就跟前面的交换
* TODO 使用的是常数空间
* 速度击败100% 内存击败45.18% 0ms
* @param nums
*/
public void sortColors1(int[] nums) {
int n = nums.length;
int p0 = 0, p1 = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] == 1) {
int temp = nums[i];
nums[i] = nums[p1];
nums[p1] = temp;
++p1;
} else if (nums[i] == 0) {
int temp = nums[i];
nums[i] = nums[p0];
nums[p0] = temp;
if (p0 < p1) {
temp = nums[i];
nums[i] = nums[p1];
nums[p1] = temp;
}
++p0;
++p1;
}
}
}
}

View File

@ -0,0 +1,251 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-08 11:19
*@Description:
* TODO 力扣76题 最小覆盖子串:
* 给你一个字符串 s 一个字符串 t 返回 s 中涵盖 t 所有字符的最小子串如果 s 中不存在涵盖 t 所有字符的子串则返回空字符串 ""
* 注意
* 对于 t 中重复字符我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量
* 如果 s 中存在这样的子串我们保证它是唯一的答案
*@Version: 1.0
*/
public class T35_MinWindow {
@Test
public void test() {
String s = "ADOBECODEBANC";
String t = "ABC";
System.out.println(minWindow2(s, t));
}
/**
* 思路:试试贪心
* 先使用map记录一下t的字母及个数
* 分别在s里面找最后一次出现的下标
* 暂时有问题 理论上来说思路是对的但是到后面需要寻找一个最短区间会比较麻烦
* @param s
* @param t
* @return
*/
public String minWindow(String s, String t) {
Map<Character, Integer> map = new HashMap<>();//<char,count>
//记录每个出现的次数
for (int i = 0; i < t.length(); i++) {
char c = t.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
}
Map<List<Integer>, Integer> list = new HashMap<>();//记录各个字母出现的位置
//寻找每个字母在s中出现的次数
for (Map.Entry<Character, Integer> entry : map.entrySet()) {
Character key = entry.getKey();
Integer count = entry.getValue();
int start = -1;
ArrayList<Integer> charList = new ArrayList<>();
while (true) {
start = s.indexOf(key, start + 1);//找到了就从他后面找
if (start == -1) break;//没找到直接返回
else charList.add(start);
}
list.put(charList, count);
}
//list [[0,10]->1,[3,9]->1,[5,12]->1]
int min = 0;
int max = 0;
//在list里面寻找最小和最大的值
for (Map.Entry<List<Integer>, Integer> listIntegerEntry : list.entrySet()) {
List<Integer> list1 = listIntegerEntry.getKey();
Integer count = listIntegerEntry.getValue();
if (list1.size() < count) return "";
}
return s.substring(min, max + 1);
}
//题解
/**
* 评论区模板题解:
* right扩容找范围left缩容找最优
* 速度击败63.87% 内存击败25.95% 14ms
* @param s
* @param t
* @return
*/
public String minWindow1(String s, String t) {
//1.维护两个map记录窗口中的符合条件的字符以及need的字符
Map<Character, Integer> window = new HashMap<>();
Map<Character, Integer> need = new HashMap<>();//need中存储的是需要的字符以及需要的对应的数量
for (char c : t.toCharArray())
need.put(c, need.getOrDefault(c, 0) + 1);
int left = 0, right = 0;//双指针
int count = 0;//count记录当前窗口中符合need要求的字符的数量,当count == need.size()时即可shrik窗口
int start = 0;//start表示符合最优解的substring的起始位序
int len = Integer.MAX_VALUE;//len用来记录最终窗口的长度并且以len作比较淘汰选出最小的substring的len
//一次遍历找可行解
while (right < s.length()) {
//更新窗口
char c = s.charAt(right);
right++;//窗口扩大
// window.put(c,window.getOrDefault(c,0)+1);其实并不需要将s中所有的都加入windowsmap只需要将need中的加入即可
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (need.get(c).equals(window.get(c))) {
count++;
}
}
//System.out.println****Debug位置
//shrink左边界找符合条件的最优解
while (count == need.size()) {
if (right - left < len) {//不断打擂找满足条件的len最短值,并记录最短的子串的起始位序start
len = right - left;
start = left;
}
//更新窗口这段代码逻辑几乎完全同上面的更新窗口
char d = s.charAt(left);
left++;//窗口缩小
if (need.containsKey(d)) {
//window.put(d,window.get(d)-1);bug若一进去就将window对应的键值缩小就永远不会满足下面的ifwhile也会一直执行知道left越界因此尽管和上面对窗口的处理几乎一样但是这个处理的顺序还是很关键的要细心
if (need.get(d).equals(window.get(d))) {
count--;
}
window.put(d, window.get(d) - 1);
}
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}
/**
* 自己写一遍模板题解:
* @param s
* @param t
* @return
*/
public String minWindow2(String s, String t) {
Map<Character, Integer> need = new HashMap<>();//记录t的需求
HashMap<Character, Integer> window = new HashMap<>();//记录窗口中实际存储
int left = 0;
int right = 0;//左右指针维护窗口
int count = 0;//记录凑够need里面的char的个数如果满了就证明window合适开始缩容
int len = Integer.MAX_VALUE;//记录最短长度
int curLen;
int start=0;
//遍历t记录need
for (int i = 0; i < t.length(); i++) {
char c = t.charAt(i);
need.put(c, need.getOrDefault(c, 0) + 1);
}
//左右指针开始遍历
while (right < s.length()) {
char c = s.charAt(right++);
//判断判断是不是需要的
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);//增加窗口中的个数
if (window.get(c).equals(need.get(c))) {
count++;//数量相等了count++;
}
}
//判断是否已经到达容量,若到达则开始缩容
while (count == need.size()) {
curLen = right - left;
if (curLen < len) {
len = curLen;
start=left;
}
//缩容
char c1 = s.charAt(left++);
if (need.containsKey(c1)) {
if (window.get(c1).equals(need.get(c1))) {
count--;//数量相等了count--;
}
window.put(c1, window.getOrDefault(c1, 0) - 1);//增加窗口中的个数
}
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}
/**
* 官方最快:使用count[]数组代替map本质上类似也是r扩容l缩容
* 速度击败99.34% 内存击败80.92%
* @param s
* @param t
* @return
*/
public String minWindow3(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if(sLen < tLen) return "";
int[] cnt = new int[128];
for(int i = 0; i < tLen; i++){
cnt[s.charAt(i)]--;
cnt[t.charAt(i)]++;
}
int differ = 0;
for(int i = 0; i < cnt.length; i++){
if(cnt[i] > 0) differ++;
}
if(differ == 0) return s.substring(0, tLen);
int size = Integer.MAX_VALUE;
int start = -1;
int l = 0, r = tLen;
while(r < sLen){
while(r < sLen && differ > 0){
if(cnt[s.charAt(r)] == 1){//等于1则证明之前刚好差一个直接删了
differ--;
}
cnt[s.charAt(r++)]--;
}
while(differ == 0){
if(r - l < size){
size = r - l;
start = l;
}
if(cnt[s.charAt(l)] == 0){//等于0则证明之前刚刚好
differ++;
}
cnt[s.charAt(l++)]++;
}
}
return start == -1 ? "" : s.substring(start, start + size);
}
}