🔍 常见高频算法题 Java 解法实战精讲(1):链表与数组
🧭 前言:算法面试中的链表与数组
链表和数组作为最基础的数据结构,几乎贯穿所有算法面试题。常见考点包括:
- 链表:反转、合并、找中点、环检测等
- 数组:双指针、哈希表处理、滑动窗口、前缀和等
文章目录
- [🔍 常见高频算法题 Java 解法实战精讲(1):链表与数组](#🔍 常见高频算法题 Java 解法实战精讲(1):链表与数组)
-
- [🧭 前言:算法面试中的链表与数组](#🧭 前言:算法面试中的链表与数组)
- 一、算法面试的核心思维
-
- [💡 链表与数组解题框架](#💡 链表与数组解题框架)
- [⚠️ 复杂度分析要点](#⚠️ 复杂度分析要点)
- [二、反转链表(Reverse Linked List)](#二、反转链表(Reverse Linked List))
-
- [💡 题目描述](#💡 题目描述)
- [⚙️ 迭代解法(双指针)](#⚙️ 迭代解法(双指针))
- [🔄 递归解法](#🔄 递归解法)
- [📊 复杂度对比](#📊 复杂度对比)
- [三、两数之和(Two Sum)](#三、两数之和(Two Sum))
-
- [💡 题目描述](#💡 题目描述)
- [⚙️ 哈希表解法](#⚙️ 哈希表解法)
- [⚠️ 常见陷阱](#⚠️ 常见陷阱)
- [🔄 扩展:三数之和](#🔄 扩展:三数之和)
- 四、滑动窗口(最长无重复子串)
-
- [💡 题目描述](#💡 题目描述)
- [⚙️ 滑动窗口解法](#⚙️ 滑动窗口解法)
- [📊 窗口移动示意图](#📊 窗口移动示意图)
- [⚠️ 复杂度分析](#⚠️ 复杂度分析)
- 五、题解套路总结
-
- [💡 双指针技巧矩阵](#💡 双指针技巧矩阵)
- [🔄 链表操作模板](#🔄 链表操作模板)
- [⚡️ 滑动窗口模板](#⚡️ 滑动窗口模板)
- [📌 六、结语与预告](#📌 六、结语与预告)
一、算法面试的核心思维
💡 链表与数组解题框架
问题类型 链表 数组 双指针 递归 虚拟头节点 双指针 哈希表 滑动窗口 二分查找
⚠️ 复杂度分析要点
复杂度 | 链表常见解法 | 数组常见解法 |
---|---|---|
O(1) | 指针修改 | 下标访问 |
O(n) | 遍历 | 单次遍历 |
O(n²) | 嵌套遍历 | 暴力搜索 |
O(nlogn) | 归并排序 | 排序+遍历 |
二、反转链表(Reverse Linked List)
💡 题目描述
输入:1 → 2 → 3 → 4 → 5
输出:5 → 4 → 3 → 2 → 1
⚙️ 迭代解法(双指针)
java
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next; // 1. 保存后继节点
curr.next = prev; // 2. 反转指针
prev = curr; // 3. 前移prev
curr = nextTemp; // 4. 前移curr
}
return prev;
}
🔄 递归解法
java
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode newHead = reverseList(head.next); // 递归到末尾
head.next.next = head; // 反转指针
head.next = null; // 断开原指针
return newHead;
}
📊 复杂度对比
解法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
迭代 | O(n) | O(1) | 内存受限 |
递归 | O(n) | O(n) | 代码简洁 |
三、两数之和(Two Sum)
💡 题目描述
输入:nums = [2,7,11,15], target = 9
输出:[0,1] (因为 nums[0] + nums[1] = 9)
⚙️ 哈希表解法
java
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[]{map.get(complement), i};
}
map.put(nums[i], i); // 存储值-索引对
}
throw new IllegalArgumentException("No solution");
}
⚠️ 常见陷阱
java
// 错误示范:未处理重复元素
int[] nums = {3, 3};
int target = 6;
// 正确解法:哈希表自动覆盖重复值
🔄 扩展:三数之和
java
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); // 先排序 O(nlogn)
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] == nums[i-1]) continue; // 去重
int left = i + 1, right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (left < right && nums[left] == nums[left+1]) left++; // 去重
while (left < right && nums[right] == nums[right-1]) right--; // 去重
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return res;
}
四、滑动窗口(最长无重复子串)
💡 题目描述
输入:"abcabcbb"
输出:3 (最长无重复子串"abc")
⚙️ 滑动窗口解法
java
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> map = new HashMap<>();
int maxLen = 0;
int left = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (map.containsKey(c)) {
// 关键:左指针跳到重复字符的下一位
left = Math.max(left, map.get(c) + 1);
}
map.put(c, right); // 更新字符位置
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
📊 窗口移动示意图
窗口变化 窗口2: ab 窗口1: a 窗口3: abc 窗口4: bca a b c a
⚠️ 复杂度分析
时间复杂度:O(n) 单次遍历
空间复杂度:O(min(m, n)) m为字符集大小
五、题解套路总结
💡 双指针技巧矩阵
类型 | 适用场景 | 经典例题 |
---|---|---|
同向指针 | 链表遍历 | 反转链表 |
反向指针 | 有序数组 | 两数之和 |
快慢指针 | 环检测 | 链表环 |
滑动窗口 | 子串问题 | 无重复最长子串 |
🔄 链表操作模板
java
// 虚拟头节点技巧
ListNode dummy = new ListNode(0);
dummy.next = head;
// 链表遍历模板
ListNode curr = head;
while (curr != null && curr.next != null) {
// 操作节点
curr = curr.next;
}
// 链表删除模板
prev.next = curr.next;
curr.next = null;
⚡️ 滑动窗口模板
java
public int slidingWindow(String s) {
Map<Character, Integer> map = new HashMap<>();
int left = 0, maxLen = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
// 更新窗口状态
map.put(c, map.getOrDefault(c, 0) + 1);
// 收缩窗口条件
while (窗口不满足条件) {
char d = s.charAt(left);
map.put(d, map.get(d) - 1);
left++;
}
// 更新结果
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
📌 六、结语与预告
本期我们从 链表 与 数组 两大数据结构出发,精选典型题目进行详解。
下一期我们将聚焦:栈与队列的经典题解,包括有效括号匹配、最小栈、单调栈等。