深入浅出Java算法:数组与链表处理类问题
数组和链表是最基础的数据结构,掌握它们的处理技巧是算法学习的关键。下面我将用通俗易懂的方式介绍常见的数组和链表处理问题及其解决方案。
一、数组篇
1. 两数之和
问题:在数组中找到两个数,使它们的和等于目标值
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 two sum solution");
}
2. 移动零
问题:将数组中的零移动到末尾,保持其他元素相对顺序
java
public void moveZeroes(int[] nums) {
int nonZeroIndex = 0;
// 将非零元素前移
for (int num : nums) {
if (num != 0) {
nums[nonZeroIndex++] = num;
}
}
// 剩余位置补零
while (nonZeroIndex < nums.length) {
nums[nonZeroIndex++] = 0;
}
}
3. 盛最多水的容器
问题:找出两条线,使它们与x轴构成的容器能容纳最多的水
java
public int maxArea(int[] height) {
int max = 0, left = 0, right = height.length - 1;
while (left < right) {
int area = Math.min(height[left], height[right]) * (right - left);
max = Math.max(max, area);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return max;
}
4. 三数之和
问题:找出数组中所有和为0的三元组(不重复)
java
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
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;
}
二、链表篇
1. 反转链表
问题:将单链表反转
java
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
2. 合并两个有序链表
问题:将两个升序链表合并为一个新的升序链表
java
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode current = dummy;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
current.next = l1;
l1 = l1.next;
} else {
current.next = l2;
l2 = l2.next;
}
current = current.next;
}
current.next = l1 != null ? l1 : l2;
return dummy.next;
}
3. 链表中环的检测
问题:判断链表中是否有环
java
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) return false;
slow = slow.next;
fast = fast.next.next;
}
return true;
}
4. 删除链表的倒数第N个节点
问题:删除链表的倒数第n个节点
java
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode first = dummy;
ListNode second = dummy;
// 先让first指针移动n+1步
for (int i = 1; i <= n + 1; i++) {
first = first.next;
}
// 然后两个指针一起移动,直到first到达末尾
while (first != null) {
first = first.next;
second = second.next;
}
second.next = second.next.next;
return dummy.next;
}
三、数组与链表综合问题
1. 重排链表
问题:将链表 L0→L1→...→Ln-1→Ln 重排为 L0→Ln→L1→Ln-1→L2→Ln-2→...
java
public void reorderList(ListNode head) {
if (head == null || head.next == null) return;
// 1. 找到中间节点
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 2. 反转后半部分链表
ListNode prev = null, curr = slow;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
// 3. 合并两个链表
ListNode first = head, second = prev;
while (second.next != null) {
ListNode temp1 = first.next;
ListNode temp2 = second.next;
first.next = second;
second.next = temp1;
first = temp1;
second = temp2;
}
}
2. 旋转数组
问题:将数组向右旋转k个位置
java
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
private void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
总结
处理数组和链表问题的常用技巧:
- 双指针技巧:适用于有序数组、链表反转、环检测等问题
- 哈希表辅助:快速查找元素,如两数之和问题
- 快慢指针:解决链表中间节点、环检测等问题
- 虚拟头节点:简化链表操作,避免处理头节点的特殊情况
- 递归与迭代:根据问题特点选择合适的实现方式
记住解决这类问题的关键步骤:
- 明确问题要求
- 画图辅助理解(特别是链表问题)
- 考虑边界条件(空数组/链表、单个元素等)
- 选择合适的数据结构和算法
掌握这些基础问题的解法,能够帮助你更好地应对更复杂的算法问题!