本文为视频的原稿,访问 B 站看视频,通过视频更能领会要义哦!
数组里的双指针
用暴力解法一定可解,双重循环得出结果。用双指针的方法,借助额外空间(一个变量),实现了降维优化。
左右指针,交替相向而行
两个指针,分别在数组的头和尾,都往中间移动,直到相遇。
题目特点:每个循环,仅看数组中的 2 个元素,双指针意在找到符合条件的 2 个元素,求出对应的结果。
典型例题:盛最多水的容器
// 暴力解法
public class Solution {
public int maxArea(int[] height) {
int len = height.length;
if (len < 2) {
return 0;
}
int res = 0;
for (int i = 0; i < len - 1; i++) {
for (int j = i + 1; j < len; j++) {
res = Math.max(res, Math.min(height[i], height[j]) * (j - i));
}
}
return res;
}
}
// 双指针解法
public class Solution {
public int maxArea(int[] height) {
int len = height.length;
if (len < 2) {
return 0;
}
int left = 0;
int right = len - 1;
int res = 0;
while (left < right) {
int minHeight = Math.min(height[left], height[right]);
res = Math.max(res, minHeight * (right - left));
if (height[left] == minHeight) {
left++;
} else {
right--;
}
}
return res;
}
}
模板
func template(nums[]int) {
left, right := 0, len(nums)-1
for left < right {
if () {
left++
}
if () {
right--
}
}
}
同类型练习题
🏆 5. 最长回文子串 推荐!
快慢指针,交替同向而行:滑动窗口
两个指针,都位于数组的头部,快指针走到尾部则循环结束,慢指针视条件移动。
题目特点:每个循环,看子区间是否满足某个条件,子区间是由双指针框起来,输出的是子区间的变形。
典型例题:最小覆盖子串
func minWindow(s string, t string) string {
need, window := make(map[byte]int), make(map[byte]int)
left, right := 0, 0
match := 0
res := ""
for i := 0; i < len(t); i++ {
need[t[i]]++
}
for right < len(s) {
// 1. 不满足输出条件,延长窗口
r := s[right]
right++
// 对右侧指针所对应的字符,做处理
if _, ok := need[r]; ok {
window[r]++
if window[r] == need[r] {
match++
}
}
// 2. 满足输出条件,缩短窗口
for match == len(need) {
// 更新匹配的最小子串长度
if res == "" || right-left < len(res) {
res = s[left:right]
}
l := s[left]
left++
if _, ok := need[l]; ok {
if window[l] == need[l] {
match--
}
window[l]--
}
}
}
return res
}
模板
left, right := 0, 0
for right < len(s) {
// 增大窗口范围
window.add(s[right])
right++;
// 满足某个条件,需要缩小窗口范围
for () {
window.remove(s[left])
left++;
}
}
同类型练习题
-
找到符合条件的子区间
-
子区间计数问题
前缀和
需要用到子区间和时,考虑借助一个数组,去存储前缀和,需要区别于双指针的用法。
func subarraySum(nums []int, k int) int {
ans := 0
sum := make([]int, len(nums)+1)
sum[0] = 0
for i := 0; i < len(nums); i++ {
sum[i+1] = sum[i] + nums[i]
}
i := 0
j := len(sum) - 1
for i < j {
if sum[j]-sum[i] < k {
j--
}
if sum[j]-sum[i] == k {
ans++
}
if sum[j]-sum[i] > k {
i++
}
}
return ans
}
链表里的双指针
快慢指针,快指针一次 2 步,慢指针一次 1 步。
典型例题:环形链表
func hasCycle(head *ListNode) bool {
// 快慢指针法,有环必套圈,无环快指针会到 null
// 了解即可,在实际开发中应用较少,可以用哈希表来存储,没必要检查环
fast := head
for (fast != nil && fast.Next != nil) {
fast = fast.Next.Next
head = head.Next
if (fast == head) {
return true
}
}
return false
}