力扣算法实现汇总
1.模式1:双指针(左右指针)
java
复制代码
package com.leetcode.agpattern15;
/**
* 模式1:双指针(左右指针)
*
* 核心思想:
* 用两个指针从数组/字符串的两端向中间移动,通过条件判断调整指针位置,将O(n²)的暴力解法优化为O(n)
*
* 适合场景:
* 1.数组/字符串的对称问题(如回文串、两数之和 II)
* 2.有序数组的目标值查找(如三数之和、四数之和)
* 3.数组的反转、分区问题
*/
public class TwoPointer1 {
//左右指针通用模版
public void towPointersLeftRight(int[] nums){
int left=0;
int right=nums.length-1;
while (left<right){
//核心逻辑:根据条件调整指针
boolean condition=left< left+1;
if(condition){
left++;//左指针右移
}else{
right--;//右指针左移
}
//特殊场景:去重(如三数之和)
while (left<right&&nums[left]==nums[left+1]){
left++;
}
while (left<right&&nums[right]==nums[right-1]){
right--;
}
}
}
}
/**
* 典型例题
* LeetCode 15(三数之和)、125(验证回文串)、11(盛最多水的容器)
*/
2.模式2:双指针(快慢指针)
java
复制代码
package com.leetcode.agpattern15;
/**
* 模式2:双指针(快慢指针)
* <p>
* 核心思想:
* 两个指针以不同速度遍历(快指针走2步,慢指针走1步),用于检测环、找中点、删除倒数第N个元素等
* <p>
* 适用场景:
* 1.链表判圈、找环入口
* 2. 链表找中点、删除倒数第N个节点
* 3.数组去重(如移除有序数组中的重复项)
*/
public class TwoPointer2 {
class ListNode {
int val;
ListNode next;
}
public void twoPointersSlowFast(ListNode head) {
ListNode slow = head;
ListNode fast = head;
//1.找链表中点
while (fast != null && fast.next != null) {
slow = slow.next;//慢指针走1步
fast = fast.next.next;
}
//slow即为链表中点
//2.判圈(环形链表)
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {//相遇则有环
break;
}
}
}
}
/**
* 典型例题
*
* LeetCode 141(环形链表)、142(环形链表 II)、19(删除链表的倒数第N个节点)
*/
3. 模式3:滑动窗口
java
复制代码
package com.leetcode.agpattern15;
import java.util.HashMap;
/**
* 模式3:滑动窗口
*
* 核心思想:
* 用左右指针维护一个[窗口],根据窗口内的条件动态扩大/收缩窗口,解决子数组/子串的最值问题
*
* 适合场景:
* 1. 最长无重复字符子串
* 2. 最小覆盖子串
* 3.子数组的和/乘积满足条件的问题
*/
public class SlidingWindow3 {
//滑动窗口通用模版
public int slidingWindows(String s){
HashMap<Character,Integer> window=new HashMap<>();
int left=0,right=0;
int res=0;//存储结果(长度/数量)
while (right< s.length()){
char c= s.charAt(right);
right++;//扩大窗口
//窗口内数据更新逻辑
window.put(c,window.getOrDefault(c,0)+1);
//满足收缩条件时,收缩窗口
while (needShrink(window)){
char d= s.charAt(left);
left++;//收缩窗口
//窗口内数据更新逻辑
window.put(d,window.get(d)-1);
//更新结果
res=Math.max(res,right-left);
}
}
return res;
}
//收缩条件(需根据题目自定义)
private boolean needShrink(HashMap<Character, Integer> window) {
return false;//替换为实际条件(如存在重复字符、窗口和超过目标等)
}
}
/**
* 典型例题
*
* LeetCode3(无重复字符的最长子串)、76(最小覆盖子串)、209(长度最小的子数组)
*/
4. 模式4:哈希表映射
java
复制代码
package com.leetcode.agpattern15;
import java.util.HashMap;
import java.util.HashSet;
/**
* 模式4:哈希表映射
*
* 核心思想:
* 用HashMap/HashSet存储[键值对],将查找操作从O(n)优化为O(1),解决计数、去重、映射问题
*
* 适用场景:
* 1.两数之和(数值→索引映射)
* 2.字母异位词分组(排序后的字符串→原字符串列表)
* 3.频率统计(元素→出现次数)
*/
public class HashMapping4 {
//哈希表映射通用模版
public void hashMapMapping(int[] nums){
//1.计数/映射场景
HashMap<Integer,Integer> map=new HashMap<>();
for(int num:nums){
//统计频率(简化写法)
map.put(num,map.getOrDefault(num,0)+1);
}
//2.去重场景
HashSet<Integer> set=new HashSet<>();
for(int num:nums){
set.add(num);//自动去重
}
//3.查找场景
int target=9;
for(int num:nums){
if(map.containsKey(target-num)){
//找到匹配项
System.out.println(num+" + "+(target-num)+ " = "+target);
}
}
}
}
/**
*典型例题
*
* LeetCode1(两数之和)、49(字母异位词分组)、347(前K个高频元素)
*/
5. 模式5:链表虚拟头节点
java
复制代码
package com.leetcode.agpattern15;
/**
* 模式5:链表虚拟头节点
*
* 核心思想:
* 创建一个虚拟头节点(dummy)指向真实头节点,避免处理[头节点为空/需要删除头节点]的边界问题,统一链表操作逻辑
*
* 适用场景
* 1.合并两个有序链表
* 2.删除链表中的节点(尤其是头节点)
* 3.链表插入/反转的边界处理
*/
public class ListDummyNode5 {
//虚拟头节点通用模版
class ListNode{
int val;
ListNode next;
ListNode(int val){
this.val=val;
}
}
public ListNode dummyNodeTemplate(ListNode head){
//创建虚拟头节点(值无意义,仅用于统一逻辑)
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode curr=dummy;//游标节点
//核心操作(eg:删除值为5的节点)
while (curr.next!=null){
if(curr.next.val==5){
curr.next=curr.next.next;//删除节点
break;
}
curr=curr.next;
}
return dummy.next;//返回真实头节点
}
}
/**
*典型例题
* LeetCode21(合并两个有序链表)、19(删除链表的倒数第N个节点)、24(两两交换链表中的节点)
*/
6.模式6:二分查找(普通/边界)
java
复制代码
package com.leetcode.agpattern15;
/**
* 模式6:二分查找(普通/边界)
*
* 核心思想:
* 将有序数组分成两部分,通过比较中间值与目标值的大小,缩小查找范围,时间复杂度O(logn)
*
* 适用场景:
* 1. 有序数组的目标值查找
* 2. 找目标值的左/右边界
* 3.旋转有序数组的查找
*/
//二分查找通用模版(覆盖普通/左边界/右边界)
public class BinarySearch6 {
//1.普通二分查找(找任意匹配项)
public int binarySearch(int[] nums,int target){
int left=0,right=nums.length-1;
while (left<=right){
int mid= left+(right-left)/2;//避免溢出
if(nums[mid] == target){
return mid;//找到目标
}else if(nums[mid]<target){
left= mid+1;
}else{
right=mid-1;
}
}
return -1;//未找到
}
//2.左边界查找(找第一个>= target位置)
public int leftBound(int[] nums,int target){
int left=0,right=nums.length-1;
while(left<=right){
int mid=left+(right-right)/2;
if(nums[mid]>=target){
right=mid-1;//收缩右边界,找左边界
}else{
left=mid+1;
}
}
//检查是否越界
if(left>=nums.length||nums[left]!=target){
return -1;
}
return left;
}
//3.右边界查找(找最后一个<=target的位置)
public int rightBound(int[] nums,int target){
int left=0,right=nums.length-1;
while (left<=right){
int mid=left+(right-left)/2;
if(nums[mid]<=target){
left=mid+1;//收缩左边界,找右边界
}else{
right=mid-1;
}
}
//检查是否越界
if(right<0 || nums[right]!=target){
return -1;
}
return right;
}
}
/**
*典型例题
*LeetCode704(二分查找)、34(在排序数组中查找元素的第一个和最后一个位置)、35(搜索插入位置)
*/
7.模式7:递归与回溯
java
复制代码
package com.leetcode.agpattern15;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* 模式7:递归与回溯
*
* 核心思想
* 递归:将大问题拆解为子问题,直到触达终止条件;
* 回溯:在递归中尝试所有可能的选择,不符合条件时[回退]重新选择
*
*适用场景
* 1.排列/组合/子集问题
* 2.路径搜索(如二叉树的路径、矩阵中的路径)
* 3.棋盘问题(如N皇后、数独)
*
*/
public class RecursiveBacktrack7 {
//回溯通用模版(排列/组合/子集)
List<List<Integer>> result= new ArrayList<>();
LinkedList<Integer> path= new LinkedList<>();//当前路径
public List<List<Integer>> backtrackTemplate(int[] nums){
backtrack(nums,0);
return result;
}
private void backtrack(int[] nums, int start) {
//1.收集结果(根据题目调整:如子集需收集所有路径,排列需收集长度等于nums的路径)
result.add(new ArrayList<>(path));
//2.遍历选择列表
for(int i=start;i<nums.length;i++){
//剪枝条件(可选,如去重,限制条件)
if(i>start&&nums[i]==nums[i-1]){
continue;
}
//做出选择
path.add(nums[i]);
//递归(排列问题:start=0,需标记已使用;组合问题:start=i+1)
backtrack(nums,i+1);
//撤回选择(回溯)
path.removeLast();
}
}
}
/**
* 典型例题
* LeetCode46(全排列)、78(子集)、39(组合总和)、131(分割回文串)
*/
8.模式8:贪心算法
java
复制代码
package com.leetcode.agpattern15;
import java.util.Arrays;
/**
* 模式8:贪心算法
*
* 核心思想:
* 每一步都选择[局部最优]的解,最终期望得到[全局最优]的解(需证明贪心策略的正确性)
*
* 适用场景
* 1.找零问题、活动选择问题
* 2.区间问题(如无重叠区间、合并区间)
* 3.字符串拼接、跳跃游戏
*/
public class Greedy8 {
//贪心算法通用模版(区间问题示例)
public int greedyTemplate(int[][] intervals){
if(intervals.length==0){
return 0;
}
//1.排序(贪心策略的核心:按结束时间升序)
Arrays.sort(intervals,(a,b)->a[1]-b[1]);
int count=1;//至少选一个区间
int end=intervals[0][1];//第一个区间的结束时间
//2.遍历选择局部最优解
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]>=end){//不重叠,选择当前区间
count++;
end= intervals[i][1];//更新结束时间
}
}
return count;
}
}
/**
* 典型例题
* LeetCode455(分发饼干)、55(跳跃游戏)、435(无重叠区间)、134(加油站)
*/
9.模式9:动态规划(基础)
java
复制代码
package com.leetcode.agpattern15;
/**
* 模式9:动态规划(基础)
*
* 核心思想:将大问题拆解为子问题,存储子问题的解(DP数组),避免重复计算,核心是[状态定义]和[转移方程]
*
* 适用场景:
* 1.最值问题(如最长递增子序列、最大子数组和)
* 2.计数问题(如爬楼梯、不同路径)
* 3.背包问题(01背包、完全背包)
*
*/
public class Dp9 {
//动态规划通用模版(基础版)
public int dpTemplate(int[] nums){
//1.状态定义:dp[i]表示前i个元素的最优解(根据题目调整)
int[] dp= new int[nums.length];
//2.初始化条件
dp[0]=nums[0];
int max=dp[0];
//3.状态转移方程
for(int i=1;i<nums.length;i++){
//示例:最大子数组和的转移方程
dp[i]=Math.max(nums[i],dp[i-1]+nums[i]);
//更新最优解
max=Math.max(max,dp[i]);
}
return max;
}
//01背包模版
public int knapsack01(int[]weight,int[] value,int bagSize){
//状态定义:dp[j]表示容量为j的背包的最大价值
int[]dp=new int[bagSize+1];
//状态转移
for(int i=0;i<weight.length;i++){
//倒序遍历,避免重复选择
for(int j=bagSize;j>=weight[i];j--){
dp[j]=Math.max(dp[j],dp[j-weight[i]]+value[i]);
}
}
return dp[bagSize];
}
}
/**
*典型例题
* LeetCode70(爬楼梯)、53(最大子数组和)、300(最长递增子序列)、416(分割等和子集)
*/
10.模式10:排序(快速排序/归并排序)
java
复制代码
package com.leetcode.agpattern15;
import java.util.Arrays;
/**
*模式10:排序(快速排序/归并排序)
*
* 核心思想:
* 快速排序:分治+分区,选择基准值将数组分为两部分
* 归并排序:分治+合并,先拆分再合并有序子数组
*
*
* 适用场景
* 1.数组排序(面试手写必考)
* 2.基于排序的后续处理(如三数之和、字母异位词分组)
* 3.时间复杂度要求O(nlogn)的场景
*/
//排序模版(快速排序+归并排序)
public class Sort10 {
//1.快速排序(原地排序,不稳定)
public void quickSort(int[] nums){
quickSort(nums,0,nums.length-1);
}
private void quickSort(int[] nums, int left, int right) {
if(left>=right){
return;
}
int pivot=partition(nums,left,right);//分区
quickSort(nums,left,pivot-1);//左区间
quickSort(nums,pivot-1,right);//右区间
}
private int partition(int[] nums, int left, int right) {
int pivot=nums[left];//选左边界为基准
int i=left,j=right;
while (i<j){
//右指针找小于基准的值
while (i<j&& nums[j]>=pivot){
j--;
}
//左指针找大于基准的值
while (i<j&& nums[i]<=pivot){
i++;
}
//交换
swap(nums,i,j);
}
//基准值归位
swap(nums,left,i);
return i;
}
private void swap(int[] nums, int i, int j) {
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
//2.归并排序(稳定排序,需要额外的空间)
public void mergeSort(int[] nums){
if(nums.length<=1)
return;
int mid=nums.length/2;
int[]left= Arrays.copyOfRange(nums,0,mid);
int[] right=Arrays.copyOfRange(nums,mid,nums.length);
mergeSort(left);
mergeSort(right);
mergeSort(nums,left,right);
}
private void mergeSort(int[] res, int[] left, int[] right) {
int i=0,j=0,k=0;
while (i<left.length&&j<right.length){
res[k++]=left[i]<=right[j]?left[i++]:right[j++];
}
//处理剩余元素
while (i<left.length){
res[k++]=left[i++];
}
while (j<right.length){
res[k++]=right[j++];
}
}
}
/**
* 典型例题
* LeetCode912(排序数组)、148(排序链表)、56(合并区间)
*/
11.模式11:DFS(深度优先搜索)
java
复制代码
package com.leetcode.agpattern15;
import com.java.utils.TreeNode;
/**
* 模式11:DFS(深度优先搜索)
*
* 核心思想:
* 优先遍历[深度],递归访问节点的所有子节点,直到触达边界,再回溯处理其他分支(可递归/栈实现)
*
* 适用场景
* 1.二叉树的遍历(前/中/后序)
* 2.图的遍历(如岛屿数量、路径搜索)
* 3.组合/排列的递归求解
*
*/
//DFS通用模版(二叉树+图)
public class DFS11 {
//1.二叉树DFS(前序遍历)
public void dfsTree(TreeNode root){
if(root==null){
return;
}
//处理当前节点(前序)
System.out.println(root.val);
//递归左子树
dfsTree(root.left);
//递归右子树
dfsTree(root.right);
}
//2.图DFS(岛屿数量示例)
int[][] dirs={{-1,0},{1,0},{0,-1},{0,1}};//上下左右
public void dfsGrap(char[][] grid,int i,int j){
// 边界/终止条件
if(i<0|| i>= grid.length|| j<0 || j>=grid[0].length|| grid[i][j]=='0'){
return;
}
//标记已访问
grid[i][j]='0';
//遍历所有方向
for(int[] dir:dirs){
int x= i+dir[0];
int y= j+ dir[1];
dfsGrap(grid,x,y);
}
}
}
/**
* 典型例题
* LeetCode94(二叉树中序遍历)、200(岛屿数量)、112(路径总和)
*/
12.BFS(广度优先搜索)
java
复制代码
package com.leetcode.agpattern15;
import com.leetcode.TreeNode;
import java.util.*;
/**
* BFS(广度优先搜索)
*
* 核心思想:
* 优先遍历[广度],用队列存储当前层的所有节点,遍历完当前层再遍历下一层,适合找[最短路径/最小深度]
*
* 适用场景
* 1.二叉树的层序遍历
* 2.图的最短路径(如单词接龙、迷宫问题)
* 3.层级相关的问题(如二叉树的最大深度、找每一层的平均值)
*/
//BFS通用模版(二叉树+图)
public class BFS12 {
//1.二叉树(层序遍历)
public List<List<Integer>> bfsTree(TreeNode root){
List<List<Integer>> result= new ArrayList<>();
if(root==null){
return result;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
int size=queue.size();//当前层的节点数
List<Integer> level=new ArrayList<>();
//遍历当前层
for(int i=0;i<size;i++){
TreeNode curr=queue.poll();
level.add(curr.val);
//加入下一层节点
if(curr.left!=null){
queue.offer(curr.left);
}
if(curr.right!=null){
queue.offer(curr.right);
}
}
result.add(level);
}
return result;
}
//2.图BFS(最短路径示例)
public int bfsGraph(String beginWord,String endWord,List<String> wordList){
Set<String> set=new HashSet<>(wordList);
if(!set.contains(endWord)){
return 0;
}
Queue<String> queue=new LinkedList<>();
queue.offer(beginWord);
int step=1;//步长
while (!queue.isEmpty()){
int size= queue.size();
for(int i=0;i<size;i++){
String curr=queue.poll();
//处理当前节点(示例:替换字符找下一个单词)
char[] chars=curr.toCharArray();
for(int j=0;j<chars.length;j++){
char temp=chars[j];
for(char c='a';c<= 'z';c++){
chars[j]=c;
String next=new String(chars);
if(next.equals(endWord)){
return step+1;
}
if(set.contains(next)){
queue.offer(next);
set.remove(next);//标记已访问
}
}
chars[j]=temp;//恢复
}
}
step++;
}
return 0;
}
}
/**
* 典型例题
* LeetCode102(二叉树的层序遍历)、127(单词接龙)、104(二叉树的最大深度)
*/
13.模式13:前缀和
java
复制代码
package com.leetcode.agpattern15;
import java.util.HashMap;
/**
* 模式13:前缀和
*
* 核心思想:
* 预处理一个[前缀和数组],prefix[i]表示前i个元素的和,通过prefix[j]-prefix[i]快速计算区间[i,j)的和,将区间和查询从O(n)优化为O(1)
*
* 适用场景:
* 1.数组的区间和查询
* 2.子数组的和等于目标值的问题
* 3.前缀和+哈希表解决子数组和问题
*/
//前缀和通用模版
public class PrefixSum13 {
//1.基础前缀和(区间和查询)
public int[] prefixSum(int[] nums){
int n=nums.length;
int[] prefix=new int[n+1];//prefix[0]=0,方便计算
for(int i=0;i<n;i++){
prefix[i+1]=prefix[i]+nums[i];
}
//区间[left,right]的和=prefix[right+1]-prefix[left]
int left=1,right=3;
int sum= prefix[right+1]-prefix[left];
return prefix;
}
//2.前缀和+哈希表(子数组和等于k)
public int subarraySum(int[] nums,int k){
HashMap<Integer,Integer> map=new HashMap<>();
map.put(0,1);//前缀和为0的次数初始化为1
int prefixSum=0,count=0;
for(int num:nums){
prefixSum+=num;
//查找是否存在prefixSum-k
if(map.containsKey(prefixSum-k)){
count+= map.get(prefixSum-k);
}
//统计当前前缀和的次数
map.put(prefixSum,map.getOrDefault(prefixSum,0)+1);
}
return count;
}
}
/**
* 典型例题
*LeetCode303(区域和检索-数组不可变)、560(和为K的子数组)、974(和可被K整除的子数组)
*/
14.模式14:差分数组
java
复制代码
package com.leetcode.agpattern15;
/**
* 模式14:差分数组
*
* 核心思想:
* 预处理一个[差分数组],diff[i]=nums[i]-nums[i-1],通过对差分数组的[区间更新],再还原为原数组,将多次区间加减操作从O(n)优化为O(1)
*
*
* 适用场景:
* 1.多次对数组的某个区间进行加减操作
* 2.区间更新后求最终数组
*
*/
//查分数组通用模版
public class Diff14 {
private int[] diff;//差分数组
//初始化差分数组
public Diff14(int[] nums){
diff=new int[nums.length];
diff[0]=nums[0];
for(int i=1;i<nums.length;i++){
diff[i]=nums[i]-nums[i-1];
}
}
//对区间[i,j]增加val(val可为负数)
public void increment(int i,int j,int val){
diff[i]+=val;
if(j+1<diff.length){
diff[j+1] -=val;
}
}
//还原为原数组
public int[] restore(){
int[] res=new int[diff.length];
res[0]=diff[0];
for(int i=1;i<diff.length;i++){
res[i]= res[i-1] + diff[i];
}
return res;
}
//eg:多次区间更新
public static void main(String[] args) {
int[] nums={1,2,3,4,5};
Diff14 dt=new Diff14(nums);
dt.increment(1,3,2);//区间[1,3]加2
dt.increment(0,2,-1);//区间[0,2]减1
int[] res=dt.restore();//结果:// 结果:[0,3,4,6,5]
for (int re : res) {
System.out.println("re = " + re);
}
}
}
/**
* 典型例题
* LeetCode1109(航班预订统计)、370(区间加法)
*/
15. 模式15:堆(优先队列)
java
复制代码
package com.leetcode.agpattern15;
import java.util.Comparator;
import java.util.HashMap;
import java.util.PriorityQueue;
/**
* 模式15:堆(优先队列)
*
* 核心思想:
* 用堆(优先队列)维护一组元素,快速获取[最大值/最小值],时间复杂度O(logn),适合处理[TopK][中位数]等问题
*
* 适用场景
* 1.前K个高频元素
* 2.数据流的中位数
* 3.合并K个有序链表
*/
//堆(优先队列)通用模版
public class Heap15 {
//1.小顶堆(找前k大的元素)
public int[] topKFrequent(int[] nums,int k){
//统计频率
HashMap<Integer,Integer> freqMap= new HashMap<>();
for(int num:nums){
freqMap.put(num,freqMap.getOrDefault(num,0)+1);//getOrDefault:默认找不到为0,能找到+1
}
//小顶堆:按频率升序,维护前k个元素
PriorityQueue<Integer> minHeap=new PriorityQueue<>((a,b)->freqMap.get(a)-freqMap.get(b));
for(int num:freqMap.keySet()){
minHeap.offer(num);
if(minHeap.size()>k){
minHeap.poll();//弹出最小频率的元素
}
}
//提取结果
int[] res=new int[k];
for(int i=k-1;i>=0;i--){
res[i]=minHeap.poll();
}
return res;
}
//2.大顶堆(默认:Comparator.reverseOrder())
public int findKthLargest(int[]nums,int k){
PriorityQueue<Integer> maxHeap=new PriorityQueue<>(Comparator.reverseOrder());
for(int num:nums){
maxHeap.offer(num);
}
//弹出k-1次,堆顶即为第k大
for(int i=0;i<k-1;i++){
maxHeap.poll();
}
return maxHeap.peek();//返回堆顶元素;
}
}
/**
*典型例题
* LeetCode347(前K个高频元素)、215(数组中的第K个最大元素)、295(数据流的中位数)
*/