蓝桥杯大纲知识点详解 大学C组 1. 枚举[1-3] 模板 1 2 3 4 for (int i = 0 ; i < n; i++) { }
解题思路
题目特征
时间复杂度
例题
Codeforces 1A - Theatre Square
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static void solve () { long n = FIO.l(); long k = FIO.l(); Map<Long,Long> m = new HashMap <>(); long minn = Integer.MAX_VALUE; for (long i = 0 ; i < n ; i++) { long x = FIO.l(); long t = Math.min(i + 1 , n - i); if (m.containsKey(k - x) && x * 2 != k) { minn = Math.min(minn,Math.max(m.get(k - x),t)); } if (m.containsKey(x)){ m.put(x, Math.min(m.get(x), t)); } else m.put(x,t); } FIO.P(minn == Integer.MAX_VALUE ? -1 : minn); }
LeetCode 78 - 子集 枚举所有子集
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 { List<List<Integer>> ans = new ArrayList <>(); List<Integer> path = new ArrayList <>(); int [] nums; int n; void dfs (int i) { if (i == n) { ans.add(new ArrayList <>(path)); return ; } dfs(i + 1 ); path.add(nums[i]); dfs(i + 1 ); path.remove(path.size() - 1 ); } void solve () { n = nums.length; dfs(0 ); } public List<List<Integer>> subsets (int [] nums) { this .nums = nums; solve(); return ans; } }
二进制枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public List<List<Integer>> subsets (int [] nums) { int n = nums.length; List<List<Integer>> ans = new ArrayList <>(); for (int i = 0 ; i < 1 <<n;i++) { List<Integer> path = new ArrayList <>(); for (int j = 0 ; j < n ; j ++ ) { if ((i >> j & 1 )== 1 ) { path.add(nums[j]); } } ans.add(path); } return ans; } }
Codeforces 1A - Theatre Square
1 2 3 4 5 6 static void solve () { long n = FIO.l(); long m = FIO.l(); long a = FIO.l(); FIO.P(((m + a - 1 ) / a) * ((n + a - 1 ) / a)); }
2. 排序 (1) 冒泡排序[2] 1 2 3 4 5 6 7 8 9 10 11 for (int i = 0 ; i < n-1 ; i++) { for (int j = 0 ; j < n-i-1 ; j++) { if (arr[j] > arr[j+1 ]) { int temp = arr[j]; arr[j] = arr[j+1 ]; arr[j+1 ] = temp; } } }
解题思路
题目特征
时间复杂度
例题
Codeforces 451B - Sort the Array
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 76 77 78 import java.util.*;public class Main { public static void main (String[] args) { Scanner scanner = new Scanner (System.in); int n = scanner.nextInt(); int [] a = new int [n]; int [] b = new int [n]; for (int i = 0 ; i < n; i++) { a[i] = scanner.nextInt(); b[i] = a[i]; } Map<Integer, Integer> mp = new HashMap <>(); Arrays.sort(b); for (int i = 0 ; i < n; i++) { mp.put(b[i], i); } for (int i = 0 ; i < n; i++) { a[i] = mp.get(a[i]); } int L = -1 ; for (int i = 0 ; i < n; i++) { if (a[i] != i) { L = i; break ; } } int R = -1 ; for (int i = n - 1 ; i >= 0 ; i--) { if (a[i] != i) { R = i; break ; } } if (L == -1 || R == -1 ) { System.out.println("yes" ); System.out.println(1 + " " + 1 ); } else { reverse(a, L, R); boolean ok = true ; for (int i = 0 ; i < n; i++) { if (a[i] != i) { ok = false ; break ; } } if (ok) { System.out.println("yes" ); System.out.println((L + 1 ) + " " + (R + 1 )); } else { System.out.println("no" ); } } } private static void reverse (int [] a, int L, int R) { while (L < R) { int temp = a[L]; a[L] = a[R]; a[R] = temp; L++; R--; } } }
(2) 选择排序[3] 1 2 3 4 5 6 7 8 9 10 11 12 13 for (int i = 0 ; i < n-1 ; i++) { int minIndex = i; for (int j = i+1 ; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } int temp = arr[minIndex]; arr[minIndex] = arr[i]; arr[i] = temp; }
解题思路
每次找到未排序部分的最小元素
将其放到已排序部分的末尾
题目特征
时间复杂度
(3) 插入排序[3] 1 2 3 4 5 6 7 8 9 10 for (int i = 1 ; i < n; i++) { int key = arr[i]; int j = i-1 ; while (j >= 0 && arr[j] > key) { arr[j+1 ] = arr[j]; j--; } arr[j+1 ] = key; }
解题思路
将元素插入到已排序部分的适当位置
适用于部分有序的数据
题目特征
时间复杂度
3. 搜索(bfs, dfs)[1-5] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Queue<Node> queue = new LinkedList <>(); queue.offer(startNode); visited[start] = true ; while (!queue.isEmpty()) { Node current = queue.poll(); for (Node neighbor : current.neighbors) { if (!visited[neighbor]) { visited[neighbor] = true ; queue.offer(neighbor); } } }
解题思路
使用队列实现
按层次遍历图或树
适合寻找最短路径问题
题目特征
时间复杂度
例题
LeetCode 200 - 岛屿数量
Codeforces 580C - Kefa and Park
(2) DFS模板 1 2 3 4 5 6 7 8 9 10 11 void dfs (Node node) { visited[node] = true ; for (Node neighbor : node.neighbors) { if (!visited[neighbor]) { dfs(neighbor); } } }
解题思路
使用递归或栈实现
深度优先遍历图或树
适合寻找所有可能解的问题
题目特征
时间复杂度
4. 贪心[1-5] 1 2 3 4 5 6 7 8 9 10 11 while (问题未解决) { Solution best = selectBestOption(); applySolution(best); updateProblemState(); }
解题思路
每一步选择当前最优解
不回溯,不重新考虑之前的选择
需要证明贪心选择的正确性
题目特征
问题具有最优子结构
贪心选择能得到全局最优解
常见于区间调度、背包问题等
时间复杂度
例题
LeetCode 455 - 分发饼干
Codeforces 1353B - Two Arrays And Swaps
5. 模拟[1-3] 1 2 3 4 5 6 7 8 9 10 while (条件满足) { step1(); step2(); updateState(); }
解题思路
严格按照题目描述实现
逐步模拟题目描述的过程
注意边界条件和特殊情况
题目特征
题目描述详细且步骤明确
需要精确实现特定规则
常见于游戏规则模拟、物理过程模拟等
时间复杂度
例题
蓝桥杯练习系统 - 模拟题
LeetCode 54 - 螺旋矩阵
Codeforces 4A - Watermelon
6. 二分[2-5] 1 2 3 4 5 6 7 8 9 10 11 12 13 int left = 0 , right = n - 1 ;while (left <= right) { int mid = left + (right - left) / 2 ; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1 ; } else { right = mid - 1 ; } } return -1 ;
解题思路
适用于有序数组的查找
每次将搜索范围缩小一半
注意边界条件的处理
题目特征
数据已排序或可以排序
需要高效查找(优于O(n))
常见于查找特定值或满足条件的极值
时间复杂度
例题
LeetCode 704 - 二分查找
Codeforces 812C - Sagheer and Nubian Market
7. DP(普通一维问题)[3-5] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int [] dp = new int [n];for (int i = 0 ; i < n; i++) { dp[i] = initialValue; } for (int i = 1 ; i < n; i++) { dp[i] = Math.max(dp[i-1 ] + nums[i], nums[i]); } return Arrays.stream(dp).max().getAsInt();
解题思路
定义状态dp[i]表示问题在i处的解
找出状态转移方程
初始化边界条件
按顺序计算dp数组
题目特征
问题可以分解为子问题
子问题之间有重叠
有最优子结构性质
时间复杂度
例题
蓝桥杯练习系统 - 动态规划题
LeetCode 53 - 最大子数组和
Codeforces 580A - Kefa and First Steps
8. 高精度[1-5] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static String add (String num1, String num2) { StringBuilder sb = new StringBuilder (); int carry = 0 ; int i = num1.length() - 1 , j = num2.length() - 1 ; while (i >= 0 || j >= 0 || carry != 0 ) { int x = i >= 0 ? num1.charAt(i--) - '0' : 0 ; int y = j >= 0 ? num2.charAt(j--) - '0' : 0 ; int sum = x + y + carry; sb.append(sum % 10 ); carry = sum / 10 ; } return sb.reverse().toString(); }
解题思路
使用字符串或数组表示大数
模拟手工计算过程
注意进位处理
题目特征
数字超出基本数据类型范围
需要精确计算
常见于大数加减乘除运算
时间复杂度
例题
蓝桥杯练习系统 - 高精度计算题
LeetCode 415 - 字符串相加
Codeforces 1016A - Death Note
9. 数据结构 1 2 3 4 5 6 Stack<Integer> stack = new Stack <>(); stack.push(1 ); int top = stack.peek(); int pop = stack.pop(); boolean isEmpty = stack.isEmpty();
解题思路
后进先出(LIFO)的数据结构
适用于需要回溯的场景
常用于括号匹配、表达式求值等问题
题目特征
时间复杂度
例题
蓝桥杯练习系统 - 数据结构题
LeetCode 20 - 有效的括号
Codeforces 546A - Soldier and Bananas
(2) 队列[2-5] 1 2 3 4 5 6 Queue<Integer> queue = new LinkedList <>(); queue.offer(1 ); int front = queue.peek(); int poll = queue.poll(); boolean isEmpty = queue.isEmpty();
解题思路
先进先出(FIFO)的数据结构
适用于需要按顺序处理的场景
常用于BFS、滑动窗口等问题
题目特征
需要按顺序处理元素
需要维护一个处理序列
需要广度优先遍历
时间复杂度
(3) 链表 [2-5] 1 2 3 4 5 6 7 8 9 10 11 12 13 class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } ListNode current = head;while (current != null ) { current = current.next; }
解题思路
动态数据结构,节点通过指针连接
适用于频繁插入删除的场景
常用于实现其他数据结构
题目特征
需要频繁插入删除操作
数据规模动态变化
需要灵活的内存分配
时间复杂度
10. 数学 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 boolean isPrime (int n) { if (n <= 1 ) return false ; for (int i = 2 ; i * i <= n; i++) { if (n % i == 0 ) return false ; } return true ; } int gcd (int a, int b) { return b == 0 ? a : gcd(b, a % b); } int lcm (int a, int b) { return a * b / gcd(a, b); }
解题思路
质数判断: 试除法
最大公约数: 欧几里得算法
最小公倍数: 基于最大公约数计算
适用于数论相关题目
题目特征
涉及质数、因数、倍数等问题
需要计算公约数或公倍数
常见于数学计算类题目
时间复杂度
质数判断: O(√n)
最大公约数: O(log(min(a,b)))
例题
蓝桥杯练习系统 - 数学题
LeetCode 204 - 计数质数
Codeforces 231A - Team
大学B组 11. 排序算法 (1)归并排序[4-5] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MergeSort { public static void mergeSort (int [] arr, int left, int right) { if (left < right) { int mid = (left + right) / 2 ; mergeSort(arr, left, mid); mergeSort(arr, mid + 1 , right); merge(arr, left, mid, right); } } private static void merge (int [] arr, int left, int mid, int right) { } }
解题思路 :
分治思想,先递归分解数组,再合并有序子序列
典型题目:逆序对问题、外部排序
时间复杂度:O(nlogn)
(2)快速排序[4-5] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class QuickSort { public static void quickSort (int [] arr, int low, int high) { if (low < high) { int pivot = partition(arr, low, high); quickSort(arr, low, pivot - 1 ); quickSort(arr, pivot + 1 , high); } } private static int partition (int [] arr, int low, int high) { } }
解题思路 :
选取基准值,将数组分为两部分
典型题目:TopK问题、中位数问题
时间复杂度:平均O(nlogn),最坏O(n²)
(3)桶排序[4] 1 2 3 4 5 6 7 8 public class BucketSort { public static void bucketSort (int [] arr, int bucketSize) { } }
解题思路 :
将数据分到有限数量的桶里
典型题目:数据范围已知且分布均匀的情况
时间复杂度:O(n+k)
(4)堆排序[4] 1 2 3 4 5 6 7 8 public class HeapSort { public static void heapSort (int [] arr) { } }
解题思路 :
利用堆这种数据结构设计的排序算法
典型题目:优先级队列、实时求TopK
时间复杂度:O(nlogn)
(5)基数排序[4~5] 1 2 3 4 5 6 7 public class RadixSort { public static void radixSort (int [] arr) { } }
解题思路 :
按照低位先排序,然后收集;再按高位排序
典型题目:多关键字排序(如日期、电话号码)
时间复杂度:O(n*k)
12. 搜索算法 (1)剪枝[4-6] 解题思路 :
在搜索过程中提前排除不可能的分支
典型题目:数独、八皇后问题
(2)双向BFS[5-6] 解题思路 :
从起点和终点同时开始BFS
典型题目:单词接龙、迷宫最短路径
(3)记忆化搜索[5] 1 2 3 4 5 6 7 8 9 10 11 public class Memoization { int [] memo; public int dfs (int state) { if (memo[state] != 0 ) return memo[state]; memo[state] = result; return result; } }
解题思路 :
存储中间结果避免重复计算
典型题目:斐波那契数列、爬楼梯问题
(4)迭代加深搜索[5-6] 解题思路 :
结合DFS和BFS,逐步增加深度限制
典型题目:IDA*算法、拼图问题
(5)启发式搜索[7] 解题思路 :
使用估价函数指导搜索方向
典型题目:A*算法、八数码问题
13. 动态规划 (1)背包DP[4-6] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Knapsack { public static int knapsack (int [] weights, int [] values, int capacity) { int n = weights.length; int [][] dp = new int [n + 1 ][capacity + 1 ]; for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= capacity; j++) { if (j >= weights[i - 1 ]) { dp[i][j] = Math.max(dp[i - 1 ][j], dp[i - 1 ][j - weights[i - 1 ]] + values[i - 1 ]); } else { dp[i][j] = dp[i - 1 ][j]; } } } return dp[n][capacity]; } }
解题思路 :
状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w]+v)
典型题目:01背包、完全背包、多重背包
(2)树形DP[4-6] 解题思路 :
在树结构上进行动态规划
典型题目:二叉树中的最大路径和、树的最小支配集
题目描述 给定一棵树,每个节点有权值,选择节点时不能选相邻节点,求最大权值和。
定义 dp[i][0] 表示不选节点 i 时的最大值,dp[i][1] 表示选择节点 i 时的最大值。 状态转移: dp[i][1] = sum(dp[child][0]) + val[i] dp[i][0] = sum(max(dp[child][0], dp[child][1]))
1 2 3 4 5 6 7 8 9 10 11 12 void dfs (int u, int parent) { dp[u][1 ] = val[u]; for (int v : tree[u]) { if (v != parent) { dfs(v, u); dp[u][1 ] += dp[v][0 ]; dp[u][0 ] += Math.max(dp[v][0 ], dp[v][1 ]); } } }
(3)状压DP[5-6] 解题思路 :
使用位运算表示状态
典型题目:旅行商问题、棋盘覆盖问题
例如,给定一个 n 个元素的集合,求所有子集的和。
例题
LeetCode 78 - 子集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public List<List<Integer>> subsets (int [] nums) { List<List<Integer>> res = new ArrayList <>(); int n = nums.length; for (int mask = 0 ; mask < (1 << n); mask++) { List<Integer> subset = new ArrayList <>(); for (int i = 0 ; i < n; i++) { if ((mask & (1 << i)) != 0 ) { subset.add(nums[i]); } } res.add(subset); } return res; } }
LeetCode 464 - 我能赢吗
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 { private Boolean[] memo; public boolean canIWin (int maxChoosableInteger, int desiredTotal) { if (maxChoosableInteger >= desiredTotal) return true ; if ((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal) return false ; memo = new Boolean [1 << (maxChoosableInteger + 1 )]; return dfs(0 , maxChoosableInteger, desiredTotal); } private boolean dfs (int state, int max, int remain) { if (memo[state] != null ) return memo[state]; for (int i = 1 ; i <= max; i++) { if ((state & (1 << i)) != 0 ) continue ; if (remain - i <= 0 || !dfs(state | (1 << i), max, remain - i)) { return memo[state] = true ; } } return memo[state] = false ; } }
(4)数位DP[5-6] 解题思路 :
处理数字位上的计数问题
典型题目:统计区间内满足条件的数字个数
题目描述 统计区间内满足特定条件的数字个数(如不含某数字)。 记忆化搜索:记录当前位数、前导零状态、限制状态等。 例如,统计 [a, b] 中不含数字 4 的数。
1 2 3 4 5 6 7 8 9 10 11 12 int dfs (int pos, boolean limit, boolean leadZero) { if (pos == num.length) return 1 ; if (!limit && !leadZero && dp[pos] != -1 ) return dp[pos]; int res = 0 ; int up = limit ? num[pos] : 9 ; for (int i = 0 ; i <= up; i++) { if (i == 4 ) continue ; res += dfs(pos + 1 , limit && (i == up), leadZero && (i == 0 )); } if (!limit && !leadZero) dp[pos] = res; return res; }
区间DP 解题思路
在区间上进行动态规划
典型题目:区间合并、区间最小值
例如,给定一个区间 [l, r],求区间内的最小值和最大值。
例题
LeetCode 312 - 戳气球
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 class Solution { public int maxCoins (int [] nums) { int n = nums.length; int [] arr = new int [n + 2 ]; System.arraycopy(nums, 0 , arr, 1 , n); arr[0 ] = arr[n + 1 ] = 1 ; int [][] dp = new int [n + 2 ][n + 2 ]; for (int len = 1 ; len <= n; len++) { for (int l = 1 ; l + len - 1 <= n; l++) { int r = l + len - 1 ; for (int k = l; k <= r; k++) { dp[l][r] = Math.max(dp[l][r], dp[l][k - 1 ] + dp[k + 1 ][r] + arr[l - 1 ] * arr[k] * arr[r + 1 ]); } } } return dp[1 ][n]; } }
516. 最长回文子序列 - 力(LeetCode)
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 class Solution { public int longestPalindromeSubseq (String s) { int n = s.length(); int [][] dp = new int [n][n]; for (int i = n - 1 ; i >= 0 ; i--) { dp[i][i] = 1 ; char c1 = s.charAt(i); for (int j = i + 1 ; j < n; j++) { char c2 = s.charAt(j); if (c1 == c2) { dp[i][j] = dp[i + 1 ][j - 1 ] + 2 ; } else { dp[i][j] = Math.max(dp[i + 1 ][j], dp[i][j - 1 ]); } } } return dp[0 ][n - 1 ]; } }
(5)DP的常见优化[7]
斜率优化(任务安排问题)洛谷P2365 斜率优化解法
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 public class Main { public static void main (String[] args) { Scanner sc = new Scanner (System.in); int n = sc.nextInt(); long S = sc.nextLong(); long [] T = new long [n+1 ]; long [] C = new long [n+1 ]; for (int i = 1 ; i <= n; i++) { T[i] = T[i-1 ] + sc.nextLong(); C[i] = C[i-1 ] + sc.nextLong(); } long [] dp = new long [n+1 ]; Arrays.fill(dp, Long.MAX_VALUE); dp[0 ] = 0 ; Deque<Integer> q = new ArrayDeque <>(); q.addLast(0 ); for (int i = 1 ; i <= n; i++) { while (q.size() >= 2 ) { int j1 = q.pollFirst(); int j2 = q.peekFirst(); if ((dp[j2]-dp[j1]) <= (S + T[i]) * (C[j2]-C[j1])) { continue ; } else { q.addFirst(j1); break ; } } int j = q.peekFirst(); dp[i] = dp[j] + S*(C[n] - C[j]) + T[i]*(C[i] - C[j]); while (q.size() >= 2 ) { int j1 = q.pollLast(); int j2 = q.peekLast(); long dy1 = dp[j1] - dp[j2]; long dx1 = C[j1] - C[j2]; long dy2 = dp[i] - dp[j1]; long dx2 = C[i] - C[j1]; if (dy1 * dx2 >= dy2 * dx1) { continue ; } else { q.addLast(j1); break ; } } q.addLast(i); } System.out.println(dp[n]); } }
四边形不等式优化(石子合并)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int [][] dp = new int [n+1 ][n+1 ];int [][] s = new int [n+1 ][n+1 ]; int [] sum = new int [n+1 ]; for (int i = 1 ; i <= n; i++) { sum[i] = sum[i-1 ] + a[i]; s[i][i] = i; } for (int len = 2 ; len <= n; len++) { for (int i = 1 ; i <= n-len+1 ; i++) { int j = i + len - 1 ; dp[i][j] = Integer.MAX_VALUE; for (int k = s[i][j-1 ]; k <= s[i+1 ][j]; k++) { int cost = dp[i][k] + dp[k+1 ][j] + sum[j] - sum[i-1 ]; if (cost < dp[i][j]) { dp[i][j] = cost; s[i][j] = k; } } } }
单调队列优化(滑动窗口最大值)
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 public int [] maxSlidingWindow(int [] nums, int k) { int n = nums.length; int [] res = new int [n - k + 1 ]; Deque<Integer> q = new ArrayDeque <>(); for (int i = 0 ; i < n; i++) { while (!q.isEmpty() && q.peekFirst() <= i - k) { q.pollFirst(); } while (!q.isEmpty() && nums[q.peekLast()] <= nums[i]) { q.pollLast(); } q.offerLast(i); if (i >= k - 1 ) { res[i - k + 1 ] = nums[q.peekFirst()]; } } return res; }
14. 字符串算法 (1)哈希[4-5] 1 2 3 4 5 6 7 8 9 10 public class StringHash { public static long getHash (String s) { long hash = 0 ; for (char c : s.toCharArray()) { hash = hash * 131 + c; } return hash; } }
解题思路 :
将字符串映射为唯一数值
典型题目:字符串匹配、重复子串检测
(2)KMP[4-6] 1 2 3 4 5 6 7 public class KMP { public static int kmp (String text, String pattern) { } }
解题思路 :
利用部分匹配信息跳过不必要比较
典型题目:字符串匹配、循环节问题
(3)Manacher[4-6] 1 2 3 4 5 6 7 public class Manacher { public static String longestPalindrome (String s) { } }
解题思路 :
线性时间求最长回文子串
典型题目:最长回文子串、回文分割
15. 图论算法 (1)欧拉回路[5-7] 解题思路 :
图中存在经过每条边恰好一次的回路
典型题目:一笔画问题、邮路问题
(2)最小生成树[5-7] 1 2 3 4 5 6 7 8 public class Kruskal { public static int kruskal (int n, int [][] edges) { } }
解题思路 :
连接所有顶点的最小权值子图
典型题目:城市道路建设、网络布线
(3)单源最短路及差分约束系统[5-7] 1 2 3 4 5 6 7 public class Dijkstra { public static int [] dijkstra(int [][] graph, int start) { } }
解题思路 :
求从一个点到其他所有点的最短路径
典型题目:路由选择、差分约束系统
(4)拓扑序列[5-7] 解题思路 :
有向无环图的线性排序
典型题目:课程安排、任务调度
(5)二分图匹配[7] 1 2 3 4 5 6 public class Hungarian { public static int maxMatch (int [][] graph) { } }
解题思路 :
求二分图的最大匹配
典型题目:任务分配、婚姻匹配问题
(6)图的连通性问题[7] 解题思路 :
割点、桥、强连通分量
典型题目:关键节点检测、网络脆弱性分析
(7)DFS序[5-7] 解题思路 :
对树进行深度优先遍历得到的顺序
典型题目:子树查询、树链剖分
(8)最近共同祖先[5-7] 1 2 3 4 5 6 public class LCA { public static int lca (int u, int v) { } }
解题思路 :
求树中两个节点的最近公共祖先
典型题目:家族关系查询、树路径问题
16. 数学 (1) 排列组合[5-6] 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 int permutation (int n, int k) { int res = 1 ; for (int i = 0 ; i < k; i++) { res *= (n - i); } return res; } int combination (int n, int k) { if (k > n - k) k = n - k; int res = 1 ; for (int i = 1 ; i <= k; i++) { res = res * (n - k + i) / i; } return res; } int [][] initCombinationDP(int maxN) { int [][] C = new int [maxN + 1 ][maxN + 1 ]; for (int i = 0 ; i <= maxN; i++) { C[i][0 ] = 1 ; for (int j = 1 ; j <= i; j++) { C[i][j] = C[i - 1 ][j - 1 ] + C[i - 1 ][j]; } } return C; }
解题思路
排列: 考虑顺序的选择
组合: 不考虑顺序的选择
递推法和动态规划预处理
题目特征
需要计算选择方式数量
涉及概率或统计问题
常见于计数类题目
时间复杂度
递推法: O(k)
动态规划预处理: O(n^2)
(2) 二项式定理[6] 1 2 3 4 5 6 7 8 9 10 int [] binomialExpansion(int n) { int [] coefficients = new int [n + 1 ]; coefficients[0 ] = 1 ; for (int i = 1 ; i <= n; i++) { coefficients[i] = coefficients[i - 1 ] * (n - i + 1 ) / i; } return coefficients; }
解题思路
计算(x+y)^n展开式的系数
使用组合数计算各项系数
适用于多项式展开问题
题目特征
需要多项式展开
涉及组合数学问题
需要高效计算系数
时间复杂度
17. 数据结构 (1) ST表[5-6] 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 class SparseTable { int [][] st; int [] log; public SparseTable (int [] arr) { int n = arr.length; log = new int [n + 1 ]; for (int i = 2 ; i <= n; i++) { log[i] = log[i / 2 ] + 1 ; } int k = log[n] + 1 ; st = new int [n][k]; for (int i = 0 ; i < n; i++) { st[i][0 ] = arr[i]; } for (int j = 1 ; j < k; j++) { for (int i = 0 ; i + (1 << j) <= n; i++) { st[i][j] = Math.max(st[i][j - 1 ], st[i + (1 << (j - 1 ))][j - 1 ]); } } } public int query (int l, int r) { int j = log[r - l + 1 ]; return Math.max(st[l][j], st[r - (1 << j) + 1 ][j]); } }
解题思路
预处理区间最值
支持O(1)查询
适用于静态区间查询
题目特征
需要频繁查询区间最值
数据静态不修改
查询次数远大于预处理时间
时间复杂度
(2) 堆[5-6] 1 2 3 4 5 6 7 8 9 10 11 PriorityQueue<Integer> minHeap = new PriorityQueue <>(); PriorityQueue<Integer> maxHeap = new PriorityQueue <>(Collections.reverseOrder()); PriorityQueue<Item> customHeap = new PriorityQueue <>((a, b) -> a.priority - b.priority); heap.offer(1 ); int top = heap.peek(); int pop = heap.poll();
解题思路
题目特征
需要频繁获取极值
需要动态维护有序集合
常见于调度问题
时间复杂度
插入O(logn)
获取极值O(1)
删除极值O(logn)
(3) 树状数组[5-6] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class FenwickTree { int [] tree; public FenwickTree (int size) { tree = new int [size + 1 ]; } public void update (int index, int delta) { while (index < tree.length) { tree[index] += delta; index += index & -index; } } public int query (int index) { int sum = 0 ; while (index > 0 ) { sum += tree[index]; index -= index & -index; } return sum; } }
解题思路
高效维护前缀和
支持单点更新和区间查询
二进制索引技术
题目特征
需要频繁更新和查询前缀和
数据动态变化
需要高效统计
时间复杂度
研究生及大学A组 19. 字符串 (1) AC自动机[7-8] 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 76 77 78 class ACNode { ACNode[] children = new ACNode [26 ]; ACNode fail; List<Integer> output; } class ACAutomaton { ACNode root; void buildTrie (String[] patterns) { root = new ACNode (); for (int i = 0 ; i < patterns.length; i++) { ACNode current = root; for (char c : patterns[i].toCharArray()) { int index = c - 'a' ; if (current.children[index] == null ) { current.children[index] = new ACNode (); } current = current.children[index]; } if (current.output == null ) { current.output = new ArrayList <>(); } current.output.add(i); } } void buildFailureLinks () { Queue<ACNode> queue = new LinkedList <>(); root.fail = null ; queue.offer(root); while (!queue.isEmpty()) { ACNode current = queue.poll(); for (int i = 0 ; i < 26 ; i++) { if (current.children[i] != null ) { ACNode child = current.children[i]; ACNode failNode = current.fail; while (failNode != null && failNode.children[i] == null ) { failNode = failNode.fail; } child.fail = (failNode == null ) ? root : failNode.children[i]; if (child.fail.output != null ) { if (child.output == null ) { child.output = new ArrayList <>(); } child.output.addAll(child.fail.output); } queue.offer(child); } } } } List<Integer> search (String text) { List<Integer> result = new ArrayList <>(); ACNode current = root; for (char c : text.toCharArray()) { int index = c - 'a' ; while (current != null && current.children[index] == null ) { current = current.fail; } current = (current == null ) ? root : current.children[index]; if (current.output != null ) { result.addAll(current.output); } } return result; } }
解题思路
结合Trie树和KMP算法
构建失败指针实现高效多模式匹配
适用于大量模式串的匹配问题
题目特征
需要同时匹配多个模式串
文本串较长
模式串有公共前缀
时间复杂度
(2) 拓展kmp[7-8] 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 int [] extendKMP(String s, String t) { int n = s.length(), m = t.length(); int [] next = new int [m]; int [] extend = new int [n]; next[0 ] = m; int a = 0 , p = 0 ; for (int i = 1 ; i < m; i++) { if (i >= p || i + next[i - a] >= p) { if (i >= p) p = i; while (p < m && t.charAt(p) == t.charAt(p - i)) p++; next[i] = p - i; a = i; } else { next[i] = next[i - a]; } } a = p = 0 ; for (int i = 0 ; i < n; i++) { if (i >= p || i + next[i - a] >= p) { if (i >= p) p = i; while (p < n && p - i < m && s.charAt(p) == t.charAt(p - i)) p++; extend[i] = p - i; a = i; } else { extend[i] = next[i - a]; } } return extend; }
解题思路
计算字符串每个位置的最长公共前缀
利用已匹配信息优化计算
适用于字符串匹配和比较问题
题目特征
需要比较字符串的所有后缀
需要高效计算最长公共前缀
常见于字符串匹配问题
时间复杂度
22. 字符串匹配算法 (1) KMP算法[7-8] 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 int kmp (String text, String pattern) { int n = text.length(), m = pattern.length(); int [] lps = computeLPS(pattern); int i = 0 , j = 0 ; while (i < n) { if (text.charAt(i) == pattern.charAt(j)) { i++; j++; if (j == m) { return i - j; } } else { if (j != 0 ) { j = lps[j - 1 ]; } else { i++; } } } return -1 ; } int [] computeLPS(String pattern) { int m = pattern.length(); int [] lps = new int [m]; int len = 0 , i = 1 ; while (i < m) { if (pattern.charAt(i) == pattern.charAt(len)) { lps[i++] = ++len; } else { if (len != 0 ) { len = lps[len - 1 ]; } else { lps[i++] = 0 ; } } } return lps; }
解题思路
预处理模式串生成部分匹配表
利用已匹配信息避免重复比较
线性时间复杂度
题目特征
需要高效字符串匹配
模式串有重复子串
适用于大文本搜索
时间复杂度
(2) Trie树[7-8] 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 class Trie { class TrieNode { TrieNode[] children = new TrieNode [26 ]; boolean isEnd; } private TrieNode root; public Trie () { root = new TrieNode (); } public void insert (String word) { TrieNode node = root; for (char c : word.toCharArray()) { int idx = c - 'a' ; if (node.children[idx] == null ) { node.children[idx] = new TrieNode (); } node = node.children[idx]; } node.isEnd = true ; } public boolean search (String word) { TrieNode node = root; for (char c : word.toCharArray()) { int idx = c - 'a' ; if (node.children[idx] == null ) { return false ; } node = node.children[idx]; } return node.isEnd; } }
解题思路
前缀树数据结构
高效字符串存储和检索
支持前缀匹配
题目特征
需要处理大量字符串
需要前缀匹配或自动补全
适用于字典类问题
时间复杂度
23. 高级图论算法 (1) 最小生成树[7-8] 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 int kruskal (int n, int [][] edges) { Arrays.sort(edges, (a, b) -> a[2 ] - b[2 ]); int [] parent = new int [n]; for (int i = 0 ; i < n; i++) parent[i] = i; int res = 0 , count = 0 ; for (int [] edge : edges) { int u = edge[0 ], v = edge[1 ], w = edge[2 ]; int rootU = find(parent, u); int rootV = find(parent, v); if (rootU != rootV) { parent[rootU] = rootV; res += w; count++; if (count == n - 1 ) break ; } } return res; } int find (int [] parent, int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; }
解题思路
题目特征
需要连接所有节点
要求总权重最小
适用于网络设计问题
时间复杂度
(2) 拓扑排序[7-8] 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 List<Integer> topologicalSort (int n, int [][] edges) { List<List<Integer>> graph = new ArrayList <>(); int [] inDegree = new int [n]; for (int i = 0 ; i < n; i++) graph.add(new ArrayList <>()); for (int [] edge : edges) { graph.get(edge[0 ]).add(edge[1 ]); inDegree[edge[1 ]]++; } Queue<Integer> queue = new LinkedList <>(); for (int i = 0 ; i < n; i++) { if (inDegree[i] == 0 ) queue.offer(i); } List<Integer> res = new ArrayList <>(); while (!queue.isEmpty()) { int u = queue.poll(); res.add(u); for (int v : graph.get(u)) { if (--inDegree[v] == 0 ) { queue.offer(v); } } } return res.size() == n ? res : null ; }
解题思路
计算节点入度
从入度为0的节点开始处理
适用于有向无环图
题目特征
需要处理依赖关系
检测有向图是否有环
适用于任务调度问题
时间复杂度
24. 数论算法 (1) 快速幂[7-8] 1 2 3 4 5 6 7 8 9 10 11 12 long fastPow (long base, long power, long mod) { long result = 1 ; while (power > 0 ) { if ((power & 1 ) == 1 ) { result = (result * base) % mod; } base = (base * base) % mod; power >>= 1 ; } return result; }
解题思路
利用二进制分解指数
通过平方减少乘法次数
适用于大数幂运算
题目特征
需要计算大数幂
需要取模运算
常见于加密算法等问题
时间复杂度
(2) 欧拉筛法[7-8] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 List<Integer> eulerSieve (int n) { boolean [] isPrime = new boolean [n + 1 ]; Arrays.fill(isPrime, true ); List<Integer> primes = new ArrayList <>(); for (int i = 2 ; i <= n; i++) { if (isPrime[i]) { primes.add(i); } for (int j = 0 ; j < primes.size() && i * primes.get(j) <= n; j++) { isPrime[i * primes.get(j)] = false ; if (i % primes.get(j) == 0 ) { break ; } } } return primes; }
解题思路
线性筛法求素数
每个合数只被最小质因数筛除
适用于大规模素数筛选
题目特征
需要高效生成素数表
需要处理大范围素数问题
适用于数论相关问题
时间复杂度
25. 高级搜索技巧 (1) 双向BFS[7-8] 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 int bidirectionalBFS (Node start, Node end) { Set<Node> visitedFromStart = new HashSet <>(); Set<Node> visitedFromEnd = new HashSet <>(); Queue<Node> queueFromStart = new LinkedList <>(); Queue<Node> queueFromEnd = new LinkedList <>(); queueFromStart.offer(start); visitedFromStart.add(start); queueFromEnd.offer(end); visitedFromEnd.add(end); int steps = 0 ; while (!queueFromStart.isEmpty() && !queueFromEnd.isEmpty()) { int size = queueFromStart.size(); for (int i = 0 ; i < size; i++) { Node current = queueFromStart.poll(); if (visitedFromEnd.contains(current)) { return steps * 2 ; } for (Node neighbor : current.neighbors) { if (!visitedFromStart.contains(neighbor)) { visitedFromStart.add(neighbor); queueFromStart.offer(neighbor); } } } steps++; size = queueFromEnd.size(); for (int i = 0 ; i < size; i++) { Node current = queueFromEnd.poll(); if (visitedFromStart.contains(current)) { return steps * 2 - 1 ; } for (Node neighbor : current.neighbors) { if (!visitedFromEnd.contains(neighbor)) { visitedFromEnd.add(neighbor); queueFromEnd.offer(neighbor); } } } steps++; } return -1 ; }
解题思路
同时从起点和终点开始搜索
减少搜索空间
适用于已知起点和终点的最短路径问题
题目特征
需要寻找两点间最短路径
搜索空间较大
适用于对称性问题
时间复杂度
(2) A*算法[7-8] 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 int aStar (Node start, Node end) { PriorityQueue<Node> openSet = new PriorityQueue <>((a, b) -> (a.g + a.h) - (b.g + b.h)); Set<Node> closedSet = new HashSet <>(); start.g = 0 ; start.h = heuristic(start, end); openSet.add(start); while (!openSet.isEmpty()) { Node current = openSet.poll(); if (current.equals(end)) { return current.g; } closedSet.add(current); for (Node neighbor : current.neighbors) { if (closedSet.contains(neighbor)) continue ; int tentativeG = current.g + distance(current, neighbor); if (!openSet.contains(neighbor) || tentativeG < neighbor.g) { neighbor.g = tentativeG; neighbor.h = heuristic(neighbor, end); neighbor.parent = current; if (!openSet.contains(neighbor)) { openSet.add(neighbor); } } } } return -1 ; }
解题思路
使用启发式函数指导搜索方向
结合Dijkstra和贪心算法
适用于有启发信息的路径规划
题目特征
需要寻找最优路径
有可用的启发式函数
适用于地图导航等问题
时间复杂度