1. 动态规划 + 状态压缩
核心思想:通过滚动数组或变量覆盖,将二维/三维DP压缩到一维,大幅降低空间复杂度。
应用场景:背包问题、路径问题等需要状态转移的场景。
示例(0-1背包问题,空间优化到O(n)):
public int knapsack(int[] weights, int[] values, int capacity) {
int[] dp = new int[capacity + 1];
for (int i = 0; i < weights.length; i++) {
for (int j = capacity; j >= weights[i]; j--) { // 逆向遍历避免覆盖
dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
}
}
return dp[capacity];
}
2. 位运算技巧
核心思想:利用位操作的特性(如异或、掩码)实现高效计算。
应用场景:状态压缩、快速判断奇偶、交换变量、找唯一数等。
示例(找数组中唯一出现一次的数,其他出现两次):
public int singleNumber(int[] nums) {
int result = 0;
for (int num : nums) {
result ^= num; // 异或性质:a ^ a = 0, a ^ 0 = a
}
return result;
}
3. 单调栈
核心思想:维护一个栈内元素单调递增或递减,快速找到下一个更大/更小元素。
应用场景:柱状图最大面积、每日温度等。
示例(下一个更大元素):
public int[] nextGreaterElement(int[] nums) {
int[] res = new int[nums.length];
Arrays.fill(res, -1);
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
int idx = stack.pop();
res[idx] = nums[i];
}
stack.push(i);
}
return res;
}
4. 并查集(Union-Find)
核心思想:通过路径压缩和按秩合并,高效处理集合的合并与查询。
应用场景:连通性问题(岛屿数量、朋友圈等)。
示例(基础并查集模板):
class UnionFind {
private int[] parent;
private int[] rank;
public UnionFind(int size) {
parent = new int[size];
rank = new int[size];
for (int i = 0; i < size; i++) {
parent[i] = i;
}
}
public int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]); // 路径压缩
}
return parent[x];
}
public void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else {
parent[rootX] = rootY;
if (rank[rootX] == rank[rootY]) {
rank[rootY]++;
}
}
}
}
}
5. 线段树(Segment Tree)
核心思想:将区间分解为树形结构,支持区间查询与更新操作。
应用场景:动态区间最值、区间和等。
示例(区间和查询):
class SegmentTree {
private int[] tree;
private int n;
public SegmentTree(int[] nums) {
n = nums.length;
tree = new int[4 * n];
build(nums, 0, 0, n - 1);
}
private void build(int[] nums, int node, int start, int end) {
if (start == end) {
tree[node] = nums[start];
} else {
int mid = (start + end) / 2;
build(nums, 2 * node + 1, start, mid);
build(nums, 2 * node + 2, mid + 1, end);
tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
}
}
public void update(int idx, int val) {
update(0, 0, n - 1, idx, val);
}
private void update(int node, int start, int end, int idx, int val) {
if (start == end) {
tree[node] = val;
} else {
int mid = (start + end) / 2;
if (idx <= mid) {
update(2 * node + 1, start, mid, idx, val);
} else {
update(2 * node + 2, mid + 1, end, idx, val);
}
tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
}
}
public int query(int l, int r) {
return query(0, 0, n - 1, l, r);
}
private int query(int node, int start, int end, int l, int r) {
if (r < start || end < l) return 0;
if (l <= start && end <= r) return tree[node];
int mid = (start + end) / 2;
return query(2 * node + 1, start, mid, l, r) +
query(2 * node + 2, mid + 1, end, l, r);
}
}
6. 快速选择(QuickSelect)
核心思想:基于快速排序的分区思想,在O(n)时间内找到第K小/大的元素。
示例(找数组中第K大的元素):
public int findKthLargest(int[] nums, int k) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int pivotIndex = partition(nums, left, right);
if (pivotIndex == k - 1) {
return nums[pivotIndex];
} else if (pivotIndex < k - 1) {
left = pivotIndex + 1;
} else {
right = pivotIndex - 1;
}
}
return -1;
}
private int partition(int[] nums, int left, int right) {
int pivot = nums[right];
int i = left;
for (int j = left; j < right; j++) {
if (nums[j] >= pivot) { // 降序排列找第K大
swap(nums, i, j);
i++;
}
}
swap(nums, i, right);
return i;
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
选择技巧的时机
- 动态规划优化:空间限制严格时(如内存敏感场景)。
- 位运算:需要极致性能或处理二进制状态时。
- 单调栈/队列:需要维护区间极值时。
- 线段树/并查集:频繁区间查询或合并操作时。