同向双指针
1. 滑动窗口问题
在一个长度为n的数组中,求满足条件的一段长度为k的子数组。
Window Sum
给一个大小为n的整型数组和一个大小为k的滑动窗口,将滑动窗口从头移到尾,输出从开始到结束每一个时刻滑动窗口内的数的和。Minimum Size Subarray Sum
给定一个由 n个正整数组成的数组和一个正整数 s ,请找出该数组中满足其和 ≥ s 的最小长度子数组。如果无解,则返回 -1。Sliding Window Median
给定一个包含 n 个整数的数组,和一个大小为k的滑动窗口,从左到右在数组中滑动这个窗口,找到数组中每个窗口内的中位数。
使用两个Heap, 一个大根堆,一个小根堆。大根堆储存小于等于当前中位数的元素,小根堆储存比当前中位数大的元素。维持大根堆的大小等于小根堆,或比小根堆多1. 这样大根堆的堆顶就是当前窗口中的中位数了。Sliding Window Maximum
给出一个可能包含重复的整数数组,和一个大小为k的滑动窗口, 从左到右在数组中滑动这个窗口,找到数组中每个窗口内的最大值。Minimum Window Substring
给定一个字符串source和一个目标字符串target,在字符串source中找到包括所有目标字符串字母的最短子串。
2. 快慢指针
在遍历链表的过程中,使用两个指针,其中一个比另一个的位置更靠前。
- Linked List Cycle II
给定一个链表,如果链表中存在环,则返回到链表中环的起始节点,如果没有环,返回null。
快慢指针,快指针一次走两步,慢指针走一步。当两指针相遇,则说明链表中有环。
相向双指针
1. 判断回文串
- Valid Palindrome
给定一个字符串,判断其是否为一个回文串。只考虑字母和数字,忽略大小写。
空串也是回文串
2. 两数和问题
- Two Sum
给一个整数数组,找到两个数使得他们的和等于一个给定的数target.
– 方法1:枚举两个数,看它们的和是否等于target。
– 时间复杂度:$O(n^2)$
– 空间复杂度:$O(1)$
– 方法2:遍历数组,用hashmap储存走过的数。如果target与当前数的差已经在hashmap中,返回这两个数的下标。
– 时间复杂度:$O(n)$
– 空间复杂度:$O(n)$
– 方法3:用自定义类储存每个数的值与下标,将数组从小到大排列。左指针从数组头开始,右指针从数组尾开始。当左右指针指向的数的和小于target的时候,左指针右移一位;大于target的时候,右指针左移一位;等于target的时候返回下标。
– 时间复杂度:$O(nlogn)$
– 空间复杂度:$O(n)$
1 | // HashMap |
Two Sum II - Input array is sorted
给定一个已经按升序排列的数组,找到两个数使他们加起来的和等于特定数。
函数应该返回这两个数的下标,index1必须小于index2。
双指针算法,在O(n)时间内完成。Two Sum - Unique Pairs
给一整数数组, 找到数组中有多少组不同的元素对有相同的和, 且和为给出的 target 值, 返回对数。
双指针算法,注意去重。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public int twoSum(int[] nums, int target) {
if (nums == null || nums.length < 2) return 0;
int ans = 0;
Arrays.sort(nums); // 因为是计数的题目,可以直接将数组重排序而不记录下标
for (int l = 0, r = nums.length - 1; l < r; r--) {
while (l < r && nums[l] + nums[r] < target) {
l++;
}
if (l < r && nums[l] + nums[r] == target) {
ans++;
while (l < r && nums[r] == nums[r - 1]) {
r--; // 去重
}
}
}
return ans;
}Two Sum - Closest to target
给定整数数组num,从中找到两个数字使得他们和最接近target,返回两数和与target的差的绝对值。
每次移动左指针或右指针的时候,都要比较两数和与target的距离。Two Sum - Less than or equal to target
给定一个整数数组,找出这个数组中有多少对的和是小于或等于目标值。返回对数。1
2
3
4
5
6
7
8
9
10
11
12
13
14public int twoSum(int[] nums, int target) {
if(nums == null || nums.length < 2) return 0;
Arrays.sort(nums);
int ans = 0;
for (int l = 0, r = nums.length - 1; l < r; ) {
if (nums[l] + nums[r] <= target) {
ans += r - l; // 当前l下,所有小于r的数与l的和都满足条件
l++;
} else {
r--;
}
}
return ans;
}Two Sum - Greater than target
给一组整数,问能找出多少对整数,他们的和大于一个给定的目标值。
类似于上题。Two Sum - Difference equals to target
给定一个整数数组,找到两个数的差等于目标值。index1必须小于index2。
和第一个Two Sum类似。
HashMap解法:遍历到的每一个数可能是减数也可能是被减数。
双指针解法:套模板就可以。Three Sum
给出一个有n个整数的数组S,在S中找到三个整数a, b, c,找到所有使得a + b + c = 0的三元组。
转化为a + b = -c,变为two sum问题。注意去重。
时间复杂度 - $O(n^2)$
空间复杂度 - $O(1)$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
26public class Solution {
public List<List<Integer>> threeSum(int[] numbers) {
List<List<Integer>> ans = new ArrayList<>();
if (numbers == null || numbers.length < 3) return ans;
Arrays.sort(numbers);
for (int i = 0; i < numbers.length - 2; i++) {
if (i > 0 && numbers[i] == numbers[i - 1]) continue; // 去重
twoSum(numbers, -numbers[i], i + 1, ans);
}
return ans;
}
private void twoSum(int[] numbers, int target, int st, List<List<Integer>> ans) {
for (int l = st, r = numbers.length - 1; l < r; r--) {
while (l < r && numbers[l] + numbers[r] < target) {
l++;
}
if (l != r && numbers[l] + numbers[r] == target) {
List<Integer> item = Arrays.asList(-target, numbers[l], numbers[r]);
ans.add(item);
while (l < r && numbers[r] == numbers[r - 1]) {
r--; // 去重
}
}
}
}
}
3. 分区问题
借鉴了快速排序的思想,双指针分区。
- QuickSelect
在数组中找到第 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
27
28
29
30
31
32
33
34public class Solution {
public int kthLargestElement(int n, int[] nums) {
return helper(nums, 0, nums.length - 1, n);
}
private int helper(int[] nums, int left, int right, int k) {
int l = left, r = right;
int pivot = nums[l];
while (l <= r) {
while (l <= r && nums[l] > pivot) {
l++;
}
while (l <= r && nums[r] < pivot) {
r--;
}
if (l <= r) {
int tmp = nums[l];
nums[l] = nums[r];
nums[r] = tmp;
l++;
r--;
}
}
// 第k大元素在左边
if (left + k - 1 <= r) {
return helper(nums, left, r, k);
}
// 第k大元素在右边
if (left + k - 1 >= l) {
return helper(nums, l, right, k - (l - left));
}
// 第k大元素在左右指针中间
return nums[r + 1];
}
}