leecode更新
This commit is contained in:
parent
6eafee1860
commit
1e6629760f
|
|
@ -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];
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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对应的键值缩小,就永远不会满足下面的if,while也会一直执行,知道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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue