力扣算法题——这里都是自己做过的,打算从2025年8月开始能保持在一天一道的频率。力扣热门100题进行了标注。我看热门题里面二叉树和动态规划是不太熟的,要抽时间花功夫学习一下。

哈希

2025-07-16 1. 两数之和简单

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

题解:用hash值中的containskey方法,遍历一遍数组,存在和target-num1一样大的数字num2,代表num1+num2=target,value记录下标。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
int len = nums.length;
for(int i=0;i<len;i++){
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return new int[]{0};
}
}

2025-07-16 49. 字母异位词分组中等

题解:遍历子串数组,全部按acsill码排序。排好序向map中添加,存在就列表添加,不存在创建key,打印value

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String, List<String>> map = new HashMap<>();
for (String str : strs) {
//排序 eat -> aet
char[] chars = str.toCharArray();
Arrays.sort(chars);
String key = new String(chars);
List<String> listRes = map.getOrDefault(key, new ArrayList<>());
listRes.add(str);
map.put(key,listRes);
}
return new ArrayList<>(map.values());
}
}

2025-07-22 128. 最长连续序列中等

题解:先将所有数字放hashset中,存在遍历set,存在当前数字小1的中断当前循环。这样能最小开始算,当有比当前数字大1的while循环,一直+1,直到不存在,记录长度。这样就能找到最长连续的整数长度。

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public int longestConsecutive(int[] nums) {
int length = nums.length;
if (length<=1) return length;
Set<Integer> numSet = new HashSet<>();
for (int num : nums) {
numSet.add(num);
}
int longestStreak=0;
int currentStreak=1;
for (int num : numSet){
if (numSet.contains(num-1)){
continue;
}
int currentNum = num;
currentStreak=1;
while (numSet.contains(currentNum+1)){
currentNum++;
currentStreak++;
}
longestStreak = Math.max(longestStreak, currentStreak);
}
return longestStreak;
}
}

双指针

2025-07-06 283. 移动零简单

题解:把遍历的所有非零数给新数组,两个数组一样长,新数组没赋值的后面全0

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public void moveZeroes(int[] nums) {
if(nums==null) {
return;
}
//第一次遍历的时候,j指针记录非0的个数,只要是非0的统统都赋给nums[j]
int j = 0;
for(int i=0;i<nums.length;++i) {
if(nums[i]!=0) {
nums[j++] = nums[i];
}
}
//非0元素统计完了,剩下的都是0了
//所以第二次遍历把末尾的元素都赋为0即可
for(int i=j;i<nums.length;++i) {
nums[i] = 0;
}
}
}

2025-07-23 11. 盛最多水的容器中等

题解:左右指针,一个在最左边一个在最右边,当左边高度比右边小,建立临时变量为当前左侧长度,一直左指针右移,直到大于外循环左侧长度统计最大值。同理当右侧小,右侧向左边移。为什么可以这样移动?因为只要某一侧移动的时候,遇到的柱子比之前短,就一定代表存储量小,底短了高也短了。

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。

image-20250814172644706

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int maxArea(int[] height) {
int left = 0;
int right = height.length-1;
int maxArea = (right-left)*Math.min(height[left], height[right]);//体积
while (left<right) {
if (height[left]<=height[right]) {
int temp = height[left];
while (left<right && height[left]<=temp) {
left++;
}
}else{
int temp = height[right];
while (left<right && height[right]<=temp) {
right--;
}
}
maxArea = Math.max(maxArea, (right-left)*Math.min(height[left], height[right]));
}
return maxArea;
}
}

2025-07-16 15. 三数之和中等

题解:找到三个数相加为0,先排序,从左向右遍历,同时设置左右指针,左指针是当前索引+1,右指针是右侧,判断内缩,大于0右收缩,小于0左收缩。为0就收集并且去重左右指针移动,去除重复值。

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.util.AbstractList;
class Solution {
public List<List<Integer>> threeSum(int[] nums) {

return new AbstractList<List<Integer>>() {

@Override
public List<Integer> get(int index) {
init();
return result.get(index);
}

@Override
public int size() {
init();
return result.size();
}

private List<List<Integer>> result;
private void init(){
if (result != null) return;
result = new ArrayList<>();
if (nums.length < 3) {
return;
}
Arrays.sort(nums);
if (nums[0] >0) return;
// 外层循环:固定三元组的第一个元素(索引i)
for (int i = 0; i < nums.length - 2; i++) {
// 左指针:i+1(第二个元素)
int left = i + 1;
// 右指针:数组末尾(第三个元素)
int right = nums.length - 1;

// 双指针遍历:寻找与nums[i]之和为0的两个元素
while (left < right) {
// 计算三数之和(用long避免int溢出)
long sum = (long) nums[i] + nums[left] + nums[right];
if (sum == 0) {
// 找到符合条件的三元组,添加到结果列表
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 去重:跳过left指针后所有与当前nums[left]相同的元素
while (left < right && nums[left] == nums[++left]);
// 去重:跳过right指针前所有与当前nums[right]相同的元素
while (left < right && nums[right] == nums[--right]);
} else if (sum < 0) {
// 和小于0:需要增大总和,左指针右移(数组已排序,右移后值变大)
// 同时跳过重复元素
while (left < right && nums[left] == nums[++left]);
} else {
// 和大于0:需要减小总和,右指针左移(左移后值变小)
// 同时跳过重复元素
while (left < right && nums[right] == nums[--right]);
}
}
// 去重:跳过外层循环中与当前nums[i]相同的元素(避免重复三元组)
while (i < nums.length - 2 && nums[i] == nums[i + 1]) {
i++;
}
}
}
};
}
}

2025-07-24 42. 接雨水困难

题解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public int trap(int[] height) {
if (height.length<=2) return 0;
//找最高柱子
int maxHeight = 0;
int maxIndex = 0;
for (int i = 0; i < height.length; i++) {
if (height[i]>maxHeight){
maxHeight = height[i];
maxIndex=i;
}
}
int res = 0;
int maxTemp=0;
int i=0;
//左侧积水
while (i<maxIndex){
if (height[i]>maxTemp) maxTemp=height[i];
res += maxTemp - height[i];
i++;
}
//右侧积水
int j=height.length-1;
maxTemp=0;
while (j>maxIndex){
if (height[j]>maxTemp) maxTemp=height[j];
res += maxTemp - height[j];
j--;
}
return res;
}
}

滑动窗口

2025-07-19 3. 无重复字符的最长子串中等

题解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
if (s.length() <= 1) return n;
char[] occ = new char[128];
char[] chars = s.toCharArray();
int maxRes = 0;
for (int left = 0,right=0; right < n; right++) {
char c = chars[right];//右侧数据
occ[c]++;//记录
//有重复的从左边去除
while (occ[c]>1){
occ[chars[left]]--;
left++;
}
maxRes = Math.max(maxRes, right - left + 1);
}
return maxRes;
}
}

2025-07-29 438. 找到字符串中所有字母异位词中等

题解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
int sLen = s.length();
int pLen = p.length();
if (sLen < pLen) return res;
int[] pCount = new int[26];
int[] sCount = new int[26];
for (int i = 0; i < pLen; i++) pCount[p.charAt(i) - 'a']++;
for(int i=0;i<sLen;i++){
sCount[s.charAt(i) - 'a']++;
//当i>=pLen的时候需要把左侧移除
if (i>=pLen) sCount[s.charAt(i-pLen) - 'a']--;
if (i>=pLen-1){
if (Arrays.equals(pCount, sCount))res.add(i-pLen+1);
}
}
return res;
}
}

子串

2025-07-31 560. 和为 K 的子数组中等

题解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int subarraySum(int[] nums, int k) {
int c = 0;
int p = 0;
Map<Integer, Integer> pc = new HashMap<>((int) (nums.length / 0.75) + 1);
pc.put(0, 1);
for (int num : nums) {
p += num;
c += pc.getOrDefault(p - k, 0);
pc.merge(p, 1, Integer::sum);
}
return c;
}
}

2025-08-01 239. 滑动窗口最大值困难

tag -——队列 数组 滑动 窗口 单调队列 堆(优先队列)

题解:用栈的思想,窗口为k位,刚开始不进while循环,当队列长度大于k以后进行操作。将队列中如果靠前的数后面有比它大的就弹出,将最大值放进res

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
/*输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]*/
public int[] maxSlidingWindow(int[] nums, int k) {
if(k==1 || nums==null) return nums;
int len=nums.length;
int[] res = new int[len-k+1];
LinkedList<Integer> queue = new LinkedList();
for(int i=0;i<len;i++){
// 保证从大到小 如果前面数小则需要依次弹出,直至满足要求
while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
queue.pollLast();
}
// 添加当前值对应的数组下标
queue.addLast(i);
// 判断当前队列中队首的值是否有效
if(queue.peek() <= i-k){
queue.poll();
}
// 当窗口长度为k时 保存当前窗口中最大值
if(i+1>=k) res[i-k+1]=nums[queue.peek()];
}
return res;
}
}

2025-08-08 76. 最小覆盖子串困难

tag——哈希表字符串滑动窗口

题解:通过distance标志位,当右指针遍历到全部的子串,进循环看左侧,左侧减少到子串的某值统计长度,当左侧等于目标子串之一,distance减少,右指针 继续找,找到distance,左侧再收缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//76 覆盖子串(滑动窗口)
public String minWindow(String s, String t) {
// 获取字符串长度
int sLen = s.length();
int tLen = t.length();
// 边界条件判断:如果s比t短,或者任一为空,则不可能存在覆盖子串
if (sLen < tLen || sLen == 0 || tLen == 0) return "";
char[] sCharArray = s.toCharArray();
char[] tCharArray = t.toCharArray();
// winFreq: 记录当前窗口中各字符出现的次数
// tFreq: 记录字符串t中各字符出现的次数
int[] winFreq = new int[64]; // 窗口
int[] tFreq = new int[64]; // 统计t中字符出现的次数和位置
// distance表示当前窗口中满足字符数量要求的字符种类数
int distance = 0;
// 初始化最小长度为一个比可能最大值更大的数
int minLen = sLen + 1;

// 统计字符串t中每个字符的出现次数
for (char c : tCharArray) {
tFreq[c - 'A']++; // 通过字符与'A'的差值作为索引
}
// 滑动窗口的左右指针
int left = 0;
int right = 0;
// 记录最小覆盖子串的起始位置
int begin = 0;
// s = "ADOBECODEBANC", t = "ABC" -> "BANC"
while (right < sLen) {
// 如果当前字符不在t中,直接跳过
if (tFreq[sCharArray[right] - 'A'] == 0) {
right++;
continue;
}
// 当窗口中该字符的数量小于t中该字符的数量时,说明这个字符是需要的,增加distance
if (winFreq[sCharArray[right] - 'A'] < tFreq[sCharArray[right] - 'A']) {
distance++;
}
// 更新窗口中该字符的计数
winFreq[sCharArray[right] - 'A']++;
right++;
// 当distance等于t的长度时,说明当前窗口已经包含了t的所有字符
while (distance == tLen) { //ADOBEC
// 记录最短长度并记录开头的下标
if (right - left < minLen) {
minLen = right - left;
begin = left;
}
// 如果左边界字符不在t中,直接移动左指针
if (tFreq[sCharArray[left] - 'A'] == 0) {
left++;
continue;
}
// 如果当前窗口中该字符的数量正好等于t中该字符的数量,
// 移除该字符后将不再满足要求,需要减少distance
if (winFreq[sCharArray[left] - 'A'] == tFreq[sCharArray[left] - 'A']) {
distance--;
}
// 更新窗口中该字符的计数
winFreq[sCharArray[left] - 'A']--;
left++;
}
}
// 如果minLen没有被更新过,说明没有找到覆盖子串
if (minLen == sLen + 1) return "";
// 返回最小覆盖子串
return s.substring(begin, begin + minLen);
}

普通数组

矩阵

链表

二叉树

图论

回溯

二分查找

贪心算法

动态规格

多维动态规划

技巧