力扣算法题——这里都是自己做过的,打算从2025年8月开始能保持在一天一道的频率。力扣热门100题进行了标注。我看热门题里面二叉树和动态规划是不太熟的,要抽时间花功夫学习一下。
哈希
给定一个整数数组 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}; } }
|
题解:遍历子串数组,全部按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) { 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()); } }
|
题解:先将所有数字放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; } }
|
双指针
题解:把遍历的所有非零数给新数组,两个数组一样长,新数组没赋值的后面全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; } int j = 0; for(int i=0;i<nums.length;++i) { if(nums[i]!=0) { nums[j++] = nums[i]; } } for(int i=j;i<nums.length;++i) { nums[i] = 0; } } }
|
题解:左右指针,一个在最左边一个在最右边,当左边高度比右边小,建立临时变量为当前左侧长度,一直左指针右移,直到大于外循环左侧长度统计最大值。同理当右侧小,右侧向左边移。为什么可以这样移动?因为只要某一侧移动的时候,遇到的柱子比之前短,就一定代表存储量小,底短了高也短了。
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。

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; } }
|
题解:找到三个数相加为0,先排序,从左向右遍历,同时设置左右指针,左指针是当前索引+1,右指针是右侧,判断内缩,大于0右收缩,小于0左收缩。为0就收集并且去重左右指针移动,去除重复值。
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != 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; for (int i = 0; i < nums.length - 2; i++) { int left = i + 1; int right = nums.length - 1;
while (left < right) { long sum = (long) nums[i] + nums[left] + nums[right]; if (sum == 0) { result.add(Arrays.asList(nums[i], nums[left], nums[right])); while (left < right && nums[left] == nums[++left]); while (left < right && nums[right] == nums[--right]); } else if (sum < 0) { while (left < right && nums[left] == nums[++left]); } else { while (left < right && nums[right] == nums[--right]); } } while (i < nums.length - 2 && nums[i] == nums[i + 1]) { i++; } } } }; } }
|
题解:
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; } }
|
滑动窗口
题解:
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; } }
|
题解:
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']++; 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; } }
|
子串
题解:
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; } }
|
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 {
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(); } if(i+1>=k) res[i-k+1]=nums[queue.peek()]; } return res; } }
|
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
| public String minWindow(String s, String t) { int sLen = s.length(); int tLen = t.length(); if (sLen < tLen || sLen == 0 || tLen == 0) return ""; char[] sCharArray = s.toCharArray(); char[] tCharArray = t.toCharArray(); int[] winFreq = new int[64]; int[] tFreq = new int[64]; int distance = 0; int minLen = sLen + 1;
for (char c : tCharArray) { tFreq[c - 'A']++; } int left = 0; int right = 0; int begin = 0; while (right < sLen) { if (tFreq[sCharArray[right] - 'A'] == 0) { right++; continue; } if (winFreq[sCharArray[right] - 'A'] < tFreq[sCharArray[right] - 'A']) { distance++; } winFreq[sCharArray[right] - 'A']++; right++; while (distance == tLen) { if (right - left < minLen) { minLen = right - left; begin = left; } if (tFreq[sCharArray[left] - 'A'] == 0) { left++; continue; } if (winFreq[sCharArray[left] - 'A'] == tFreq[sCharArray[left] - 'A']) { distance--; } winFreq[sCharArray[left] - 'A']--; left++; } } if (minLen == sLen + 1) return ""; return s.substring(begin, begin + minLen); }
|
普通数组
矩阵
链表
二叉树
图论
回溯
回溯模版以及三部曲
三部曲:函数参数和返回值、终止条件、单层搜索逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private void backtracking(int[] candidates, int target, int index, List<List<Integer>> result, List<Integer> tempList) { if (target == 0) { result.add(new ArrayList<>(tempList)); return; } for (int i = index; i < candidates.length; i++) { if (candidates[i] <= target) { tempList.add(candidates[i]); backtracking(candidates, target - candidates[i], i, result, tempList); tempList.remove(tempList.size() - 1); } } }
|
N皇后
刚开始以为很难的,梳理完思路,做起来舒服多了,先定义一个全部为.的二维数组作为棋盘。终止条件就是便利到最后一行终止做回溯,搜索逻辑中只有当棋盘位置允许皇后放置的时候才能放置。放完以后继续进入,行数+1,回溯的时候将对应位置从Q改成.即可。由于是从上往下,从左往右便利棋盘的,所以可以把判断合法函数只用检验列和左上和右上就好了。
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 69 70 71 72 73 74 75
| package org.yoki.dfs; import java.util.ArrayList; import java.util.List;
public class LC51 { public static void main(String[] args) { LC51 lc51 = new LC51(); List<List<String>> res = lc51.solveNQueens(4); System.out.println(res); } public List<List<String>> solveNQueens(int n) { List<List<String>> res = new ArrayList<>(); String[][] checkBoard = new String[n][n]; for (int i = 0; i < n; i++){ for (int j = 0; j < n; j++){ checkBoard[i][j] = "."; } } backTracking(checkBoard,0,res,new ArrayList<String>()); return res; } private void backTracking(String[][] checkBoard, int row, List<List<String>> res, List<String> path) { if (row == checkBoard.length){ res.add(new ArrayList<>(path)); return; } for (int i = 0; i < checkBoard.length; i++){ if (!isValid(checkBoard,row,i)){ continue; } checkBoard[row][i] = "Q"; StringBuilder pathString = new StringBuilder(); for (int j = 0; j < checkBoard.length; j++){ pathString.append(checkBoard[row][j]); } path.add(pathString.toString()); backTracking(checkBoard,row+1,res,path); checkBoard[row][i] = "."; path.remove(path.size()-1); } } private boolean isValid(String[][] checkBoard, int row, int col) { for (int i = 0; i < row; i++) { if ("Q".equals(checkBoard[i][col])) { return false; } } for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { if ("Q".equals(checkBoard[i][j])) { return false; } } for (int i = row - 1, j = col + 1; i >= 0 && j < checkBoard.length; i--, j++) { if ("Q".equals(checkBoard[i][j])) { return false; } } return true; } }
|
二分查找
栈
堆
贪心算法
动态规格
多维动态规划
技巧