数据结构与算法(DSA)对于高效解决问题至关重要。以下是16个关键模式,每个模式都附有使用案例和示例,帮助您解决现实世界中的问题。本指南包括简洁的Java示例,以演示每个模式的应用。
用于跟踪随时间变化的子数据集,通常在数组或字符串中使用。
使用案例:子数组的最大和。 示例:大小为K的子数组的最大和。
public int maxSubArraySum(int[] arr, int k) {
int maxSum = 0, windowSum = 0;
for (int i = 0; i < arr.length; i++) {
windowSum += arr[i];
if (i >= k - 1) {
maxSum = Math.max(maxSum, windowSum);
windowSum -= arr[i - (k - 1)];
}
}
return maxSum;
}
两个指针从数组的不同端向中间收敛,以找到解决方案。
使用案例:在已排序数组中查找对。 示例:查找两个和为目标值的数字。
public int[] twoSum(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left < right) {
int sum = arr[left] + arr[right];
if (sum == target) return new int[]{left, right};
else if (sum < target) left++;
else right--;
}
return new int[]{};
}
两个指针以不同速度移动,以检测序列中的循环。
使用案例:检测链表中的循环。 示例:检查链表是否有循环。
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
合并重叠的区间。
使用案例:安排会议。 示例:合并重叠的区间。
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> merged = new ArrayList<>();
for (int[] interval : intervals) {
if (merged.isEmpty() || merged.get(merged.size() - 1)[1] < interval[0]) {
merged.add(interval);
} else {
merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], interval[1]);
}
}
return merged.toArray(new int[merged.size()][]);
}
当元素落在一定范围内时,对数字进行排序。
使用案例:查找缺失的数字。 示例:查找1到N之间的缺失数字。
public int findMissingNumber(int[] nums) {
int i = 0;
while (i < nums.length) {
if (nums[i] != i && nums[i] < nums.length) {
int temp = nums[nums[i]];
nums[nums[i]] = nums[i];
nums[i] = temp;
} else {
i++;
}
}
for (i = 0; i < nums.length; i++) {
if (nums[i] != i) return i;
}
return nums.length;
}
在原地反转链表。
使用案例:反转链表的子列表。 示例:反转链表。
public ListNode reverseList(ListNode head) {
ListNode prev = null, current = head;
while (current != null) {
ListNode next = current.next;
current.next = prev;
prev = current;
current = next;
}
return prev;
}
逐层探索树中的节点。
使用案例:层序遍历。 示例:逐层遍历二叉树。
public List<List<Integer>> bfs(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if (root != null) queue.add(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
currentLevel.add(node.val);
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
result.add(currentLevel);
}
return result;
}
在回溯之前尽可能沿着分支深入探索。
使用案例:在树或图中搜索。 示例:查找所有根到叶的路径。
public void dfs(TreeNode node, List<Integer> path, List<List<Integer>> result) {
if (node == null) return;
path.add(node.val);
if (node.left == null && node.right == null) result.add(new ArrayList<>(path));
dfs(node.left, path, result);
dfs(node.right, path, result);
path.remove(path.size() - 1);
}
使用两个堆来维护动态数据集。
使用案例:在数据流中查找中位数。 示例:查找数据流的中位数。
class MedianFinder {
private PriorityQueue<Integer> low = new PriorityQueue<>(Collections.reverseOrder());
private PriorityQueue<Integer> high = new PriorityQueue<>();
public void addNum(int num) {
low.offer(num);
high.offer(low.poll());
if (low.size() < high.size()) low.offer(high.poll());
}
public double findMedian() {
return low.size() > high.size() ? low.peek() : (low.peek() + high.peek()) / 2.0;
}
}
生成所有可能的子集。
使用案例:组合和排列问题。 示例:查找集合的所有子集。
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
result.add(new ArrayList<>());
for (int num : nums) {
int size = result.size();
for (int i = 0; i < size; i++) {
List<Integer> subset = new ArrayList<>(result.get(i));
subset.add(num);
result.add(subset);
}
}
return result;
}
在旋转或部分排序的数组中搜索。
使用案例:在旋转排序数组中查找元素。 示例:在旋转排序数组中搜索目标。
public int search(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;
if (nums[left] <= nums[mid]) {
if (target >= nums[left] && target < nums[mid]) right = mid - 1;
else left = mid + 1;
} else {
if (target > nums[mid] && target <= nums[right]) left = mid + 1;
else right = mid - 1;
}
}
return -1;
}
使用异或解决涉及对的问题。
使用案例:查找唯一数字。 示例:查找数组中的单个数字。
public int singleNumber(int[] nums) {
int result = 0;
for (int num : nums) result ^= num;
return result;
}
使用堆查找数据集中的前K个元素。
使用案例:查找前K个频繁元素。 示例:查找K个最频繁的数字。
public List<Integer> topKFrequent(int[] nums, int k) {
Map<Integer, Integer> countMap = new HashMap<>();
for (int num : nums) countMap.put(num, countMap.getOrDefault(num, 0) + 1);
PriorityQueue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>(Comparator.comparingInt(Map.Entry::getValue));
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
heap.offer(entry);
if (heap.size() > k) heap.poll();
}
return heap.stream().map(Map.Entry::getKey).collect(Collectors.toList());
}
高效合并多个已排序的数组。
使用案例:合并K个已排序的链表。 示例:合并K个已排序的链表。
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a.val));
ListNode dummy = new ListNode(0), tail = dummy;
for (ListNode list : lists) if (list != null) heap.offer(list);
while (!heap.isEmpty()) {
tail.next = heap.poll();
tail = tail.next;
if (tail.next != null) heap.offer(tail.next);
}
return dummy.next;
}
在约束条件下优化选择。
使用案例:资源分配。 示例:解决0/1背包问题。
public 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 w = 1; w <= capacity; w++) {
if (weights[i - 1] <= w) {
dp[i][w] = Math.max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}
在有向无环图(DAG)中找到有效的任务顺序。
使用案例:课程安排。 示例:找到课程的正确顺序。
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<Integer>[] graph = new ArrayList[numCourses];
int[] inDegree = new int[numCourses];
for (int i = 0; i < numCourses; i++) graph[i] = new ArrayList<>();
for (int[] pre : prerequisites) {
graph[pre[1]].add(pre[0]);
inDegree[pre[0]]++;
}
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) if (inDegree[i] == 0) queue.add(i);
int[] result = new int[numCourses];
int idx = 0;
while (!queue.isEmpty()) {
int course = queue.poll();
result[idx++] = course;
for (int next : graph[course]) {
inDegree[next]--;
if (inDegree[next] == 0) queue.add(next);
}
}
return idx == numCourses ? result : new int[0];
}
这些16个问题解决模式对于掌握数据结构与算法至关重要。每个模式都可以应用于广泛的现实世界问题,提供了一条通向最优解决方案的高效路径。