03-双指针技巧
💡 核心结论
双指针本质
定义:用两个指针遍历,降低时间复杂度
核心:O(n²) → O(n)
类型:对撞指针、快慢指针、滑动窗口
适用:有序数组、链表、字符串
关键:明确每个指针的含义和移动条件
三种模式
模式 |
特点 |
适用 |
复杂度 |
|---|---|---|---|
对撞指针 |
两端向中间 |
有序数组、回文 |
O(n) |
快慢指针 |
同向不同速 |
链表环、中点 |
O(n) |
滑动窗口 |
维护区间 |
子串、子数组 |
O(n) |
👈👉 对撞指针
两数之和II(有序数组)
def two_sum(nums, target):
left, right = 0, len(nums) - 1
while left < right:
sum_val = nums[left] + nums[right]
if sum_val == target:
return [left, right]
elif sum_val < target:
left += 1
else:
right -= 1
return []
三数之和
def three_sum(nums):
nums.sort()
result = []
for i in range(len(nums) - 2):
# 去重
if i > 0 and nums[i] == nums[i-1]:
continue
left, right = i + 1, len(nums) - 1
while left < right:
sum_val = nums[i] + nums[left] + nums[right]
if sum_val == 0:
result.append([nums[i], nums[left], nums[right]])
# 去重
while left < right and nums[left] == nums[left+1]:
left += 1
while left < right and nums[right] == nums[right-1]:
right -= 1
left += 1
right -= 1
elif sum_val < 0:
left += 1
else:
right -= 1
return result
🐢🐰 快慢指针
环形链表
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
链表中点
def find_middle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
🪟 滑动窗口
最长无重复子串
def length_of_longest_substring(s):
char_set = set()
left = 0
max_len = 0
for right in range(len(s)):
while s[right] in char_set:
char_set.remove(s[left])
left += 1
char_set.add(s[right])
max_len = max(max_len, right - left + 1)
return max_len
滑动窗口模板
def sliding_window(s):
left = 0
window = {} # 维护窗口状态
result = 0
for right in range(len(s)):
# 扩大窗口
c = s[right]
window[c] = window.get(c, 0) + 1
# 缩小窗口
while window不满足条件:
d = s[left]
left += 1
window[d] -= 1
# 更新结果
result = max(result, right - left + 1)
return result
📚 LeetCode练习
对撞指针
快慢指针
滑动窗口
💻 完整代码实现
Python 实现
1"""
2双指针技巧实现
3"""
4
5# ========== 对撞指针 ==========
6
7def two_sum_sorted(nums, target):
8 """有序数组两数之和"""
9 left, right = 0, len(nums) - 1
10
11 while left < right:
12 total = nums[left] + nums[right]
13 if total == target:
14 return [left, right]
15 elif total < target:
16 left += 1
17 else:
18 right -= 1
19
20 return []
21
22
23def three_sum(nums):
24 """三数之和为0"""
25 nums.sort()
26 result = []
27
28 for i in range(len(nums) - 2):
29 # 去重
30 if i > 0 and nums[i] == nums[i-1]:
31 continue
32
33 left, right = i + 1, len(nums) - 1
34 while left < right:
35 total = nums[i] + nums[left] + nums[right]
36 if total == 0:
37 result.append([nums[i], nums[left], nums[right]])
38 # 去重
39 while left < right and nums[left] == nums[left+1]:
40 left += 1
41 while left < right and nums[right] == nums[right-1]:
42 right -= 1
43 left += 1
44 right -= 1
45 elif total < 0:
46 left += 1
47 else:
48 right -= 1
49
50 return result
51
52
53def container_with_most_water(height):
54 """盛最多水的容器"""
55 left, right = 0, len(height) - 1
56 max_area = 0
57
58 while left < right:
59 width = right - left
60 area = width * min(height[left], height[right])
61 max_area = max(max_area, area)
62
63 # 移动较短的那边
64 if height[left] < height[right]:
65 left += 1
66 else:
67 right -= 1
68
69 return max_area
70
71
72# ========== 快慢指针 ==========
73
74class ListNode:
75 def __init__(self, val=0, next=None):
76 self.val = val
77 self.next = next
78
79
80def has_cycle(head):
81 """检测链表环"""
82 slow = fast = head
83
84 while fast and fast.next:
85 slow = slow.next
86 fast = fast.next.next
87 if slow == fast:
88 return True
89
90 return False
91
92
93def detect_cycle(head):
94 """找环的起始节点"""
95 slow = fast = head
96
97 # 找相遇点
98 while fast and fast.next:
99 slow = slow.next
100 fast = fast.next.next
101 if slow == fast:
102 break
103 else:
104 return None
105
106 # 找起始点
107 slow = head
108 while slow != fast:
109 slow = slow.next
110 fast = fast.next
111
112 return slow
113
114
115def find_middle(head):
116 """找链表中点"""
117 slow = fast = head
118
119 while fast and fast.next:
120 slow = slow.next
121 fast = fast.next.next
122
123 return slow
124
125
126# ========== 滑动窗口 ==========
127
128def length_of_longest_substring(s):
129 """最长无重复字符子串"""
130 char_set = set()
131 left = 0
132 max_len = 0
133
134 for right in range(len(s)):
135 while s[right] in char_set:
136 char_set.remove(s[left])
137 left += 1
138 char_set.add(s[right])
139 max_len = max(max_len, right - left + 1)
140
141 return max_len
142
143
144def min_window(s, t):
145 """最小覆盖子串"""
146 from collections import Counter
147
148 need = Counter(t)
149 window = {}
150 left = 0
151 valid = 0
152 start, length = 0, float('inf')
153
154 for right in range(len(s)):
155 c = s[right]
156 window[c] = window.get(c, 0) + 1
157
158 if c in need and window[c] == need[c]:
159 valid += 1
160
161 while valid == len(need):
162 if right - left + 1 < length:
163 start = left
164 length = right - left + 1
165
166 d = s[left]
167 left += 1
168 if d in need and window[d] == need[d]:
169 valid -= 1
170 window[d] -= 1
171
172 return s[start:start + length] if length != float('inf') else ""
173
174
175def find_anagrams(s, p):
176 """找所有字母异位词"""
177 from collections import Counter
178
179 need = Counter(p)
180 window = {}
181 left = 0
182 valid = 0
183 result = []
184
185 for right in range(len(s)):
186 c = s[right]
187 window[c] = window.get(c, 0) + 1
188
189 if c in need and window[c] == need[c]:
190 valid += 1
191
192 while right - left + 1 >= len(p):
193 if valid == len(need):
194 result.append(left)
195
196 d = s[left]
197 left += 1
198 if d in need and window[d] == need[d]:
199 valid -= 1
200 window[d] -= 1
201
202 return result
203
204
205def demo():
206 """演示双指针"""
207 print("=== 双指针技巧演示 ===\n")
208
209 # 两数之和
210 nums = [2, 7, 11, 15]
211 target = 9
212 print(f"两数之和 {nums}, target={target}:")
213 print(f" 结果: {two_sum_sorted(nums, target)}\n")
214
215 # 三数之和
216 nums = [-1, 0, 1, 2, -1, -4]
217 print(f"三数之和 {nums}:")
218 print(f" 结果: {three_sum(nums)}\n")
219
220 # 盛水
221 height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
222 print(f"盛最多水 {height}:")
223 print(f" 最大面积: {container_with_most_water(height)}\n")
224
225 # 最长无重复子串
226 s = "abcabcbb"
227 print(f"最长无重复子串 '{s}':")
228 print(f" 长度: {length_of_longest_substring(s)}\n")
229
230 # 字母异位词
231 s, p = "cbaebabacd", "abc"
232 print(f"找字母异位词 s='{s}', p='{p}':")
233 print(f" 起始索引: {find_anagrams(s, p)}")
234
235
236if __name__ == '__main__':
237 demo()
238
C++ 实现
1/**
2 * 双指针技巧实现
3 */
4
5#include <iostream>
6#include <vector>
7#include <unordered_set>
8#include <algorithm>
9
10using namespace std;
11
12// 两数之和(有序数组)
13vector<int> twoSum(const vector<int>& nums, int target) {
14 int left = 0, right = nums.size() - 1;
15
16 while (left < right) {
17 int sum = nums[left] + nums[right];
18 if (sum == target) {
19 return {left, right};
20 } else if (sum < target) {
21 left++;
22 } else {
23 right--;
24 }
25 }
26
27 return {};
28}
29
30// 三数之和
31vector<vector<int>> threeSum(vector<int>& nums) {
32 vector<vector<int>> result;
33 sort(nums.begin(), nums.end());
34
35 for (int i = 0; i < (int)nums.size() - 2; i++) {
36 if (i > 0 && nums[i] == nums[i-1]) continue;
37
38 int left = i + 1, right = nums.size() - 1;
39 while (left < right) {
40 int sum = nums[i] + nums[left] + nums[right];
41 if (sum == 0) {
42 result.push_back({nums[i], nums[left], nums[right]});
43 while (left < right && nums[left] == nums[left+1]) left++;
44 while (left < right && nums[right] == nums[right-1]) right--;
45 left++;
46 right--;
47 } else if (sum < 0) {
48 left++;
49 } else {
50 right--;
51 }
52 }
53 }
54
55 return result;
56}
57
58// 盛最多水的容器
59int maxArea(const vector<int>& height) {
60 int left = 0, right = height.size() - 1;
61 int maxWater = 0;
62
63 while (left < right) {
64 int width = right - left;
65 int h = min(height[left], height[right]);
66 maxWater = max(maxWater, width * h);
67
68 if (height[left] < height[right]) {
69 left++;
70 } else {
71 right--;
72 }
73 }
74
75 return maxWater;
76}
77
78// 最长无重复字符子串
79int lengthOfLongestSubstring(const string& s) {
80 unordered_set<char> charSet;
81 int left = 0;
82 int maxLen = 0;
83
84 for (int right = 0; right < s.length(); right++) {
85 while (charSet.count(s[right])) {
86 charSet.erase(s[left]);
87 left++;
88 }
89 charSet.insert(s[right]);
90 maxLen = max(maxLen, right - left + 1);
91 }
92
93 return maxLen;
94}
95
96int main() {
97 cout << "=== 双指针技巧演示 ===" << endl << endl;
98
99 // 两数之和
100 vector<int> nums = {2, 7, 11, 15};
101 int target = 9;
102 cout << "两数之和 [2,7,11,15], target=9:" << endl;
103 vector<int> indices = twoSum(nums, target);
104 cout << " 结果: [" << indices[0] << ", " << indices[1] << "]" << endl << endl;
105
106 // 三数之和
107 vector<int> nums2 = {-1, 0, 1, 2, -1, -4};
108 cout << "三数之和 [-1,0,1,2,-1,-4]:" << endl;
109 vector<vector<int>> triplets = threeSum(nums2);
110 cout << " 结果: [";
111 for (size_t i = 0; i < triplets.size(); i++) {
112 cout << "[" << triplets[i][0] << "," << triplets[i][1] << "," << triplets[i][2] << "]";
113 if (i < triplets.size() - 1) cout << ", ";
114 }
115 cout << "]" << endl << endl;
116
117 // 盛水
118 vector<int> height = {1, 8, 6, 2, 5, 4, 8, 3, 7};
119 cout << "盛最多水 [1,8,6,2,5,4,8,3,7]:" << endl;
120 cout << " 最大面积: " << maxArea(height) << endl << endl;
121
122 // 最长无重复子串
123 string s = "abcabcbb";
124 cout << "最长无重复子串 '" << s << "':" << endl;
125 cout << " 长度: " << lengthOfLongestSubstring(s) << endl;
126
127 return 0;
128}