【LeetCode】双指针 two_pointers(共47题)
【3】Longest Substring Without Repeating Characters
【11】Container With Most Water
【15】3Sum (2019年2月26日)
给了一个乱序的数组,返回一个结果数组,数组里面每个元素是一个三元组, 三元组的和加起来为0。
题解:先固定第一个数,然后后面两个数的控制用夹逼定理,2 pointers 来解。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 vector<vector<int>> threeSum(vector<int>& nums) { 4 const int n = nums.size(); 5 vector<vector<int>> res; 6 if (nums.size() < 3) {return res;} 7 sort(nums.begin(), nums.end()); 8 for (int i = 0 ; i < n; ) { 9 int left = i + 1, right = n - 1; 10 while (left < right) { 11 int sum = nums[i] + nums[left] + nums[right]; 12 if (sum == 0) { 13 res.push_back(vector<int>{nums[i], nums[left], nums[right]}); 14 ++left, --right; 15 while (left < right && nums[left] == nums[left-1]) { ++left; } 16 while (left < right && nums[right] == nums[right+1]) { --right; } 17 } else if (sum < 0) { 18 ++left; 19 while (left < right && nums[left] == nums[left-1]) { ++left; } 20 } else { 21 --right; 22 while (left < right && nums[right] == nums[right+1]) { --right; } 23 } 24 } 25 ++i; 26 while (i < n && nums[i] == nums[i-1]) {++i;} 27 } 28 return res; 29 } 30 };
【16】3Sum Closest (2019年2月26日)
【18】4Sum ()
【19】Remove Nth Node From End of List
【26】Remove Duplicates from Sorted Array
【27】Remove Element
【28】Implement strStr()
【30】Substring with Concatenation of All Words
【42】Trapping Rain Water
【61】Rotate List
【75】Sort Colors
【76】Minimum Window Substring (2019年1月13日,算法群,第一次写,需要复习)
给了两个字符串 S 和 T, 在S中求一个最短子串,这个字串需要包含 T 中的所有字母。
题解: sliding window, (有两种,窗口大小可变和窗口大小不变的)。用 2 pointers 模拟窗口。时间复杂度是 O(N)。用一个 hash-map mp 记录 t 中的字母频次。用一个 count 变量记录已经满足了的 字母数量。当 mp.size() == count 的时候就可以考虑缩小窗口。https://www.youtube.com/watch?v=9qFR2WQGqkU
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 string minWindow(string s, string t) { 4 const int ssize = s.size(), tsize = t.size(); 5 if (ssize < tsize) { 6 return ""; 7 } 8 unordered_map<char, int> mp; 9 for (auto c : t) { 10 mp[c]++; 11 } 12 int slow = 0, fast = 0, minLen = INT_MAX, start = 0, count = 0; 13 for (; fast < ssize; ++fast) { 14 char c = s[fast]; 15 if (mp.find(c) == mp.end()) { 16 continue; 17 } 18 mp[c]--; 19 if (mp[c] == 0) { count++; } 20 while (count == mp.size()) { 21 if (fast - slow + 1 < minLen) { 22 minLen = fast - slow + 1; 23 start = slow; 24 } 25 char c1 = s[slow++]; 26 if (mp.find(c1) == mp.end()) { continue; } 27 mp[c1]++; 28 if (mp[c1] == 1) { 29 count--; 30 } 31 } 32 } 33 return minLen == INT_MAX ? "" : s.substr(start, minLen); 34 } 35 };
【80】Remove Duplicates from Sorted Array II
【86】Partition List
【88】Merge Sorted Array
【125】Valid Palindrome
【141】Linked List Cycle
【142】Linked List Cycle II
【159】Longest Substring with At Most Two Distinct Characters
【167】Two Sum II - Input array is sorted
【209】Minimum Size Subarray Sum (2019年1月11日,算法群)
给了 n 个正整数 nums 数组,和一个正整数 s,要求返回一个最短连续子数组的长度,使得这个子数组的和大于等于s。
follow-up 是 如果想出了 O(n) 的做法,能不能想出 O(nlogn) 的做法。
题解:O(N) 的做法是 2 pointers, O(nlogn) 的做法是 前缀和 + 二分
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int minSubArrayLen(int s, vector<int>& nums) { 4 const int n = nums.size(); 5 int p1 = 0, p2 = 0; 6 int ans = n + 1, cur_sum = 0; 7 while (p1 <= p2 && p2 < n) { 8 cur_sum += nums[p2]; 9 while (p1 < p2 && cur_sum - nums[p1] >= s) { 10 cur_sum -= nums[p1]; 11 ++p1; 12 } 13 if (cur_sum >= s) { 14 ans = min(p2 - p1 + 1, ans); 15 } 16 ++p2; 17 } 18 return ans == n + 1 ? 0 : ans; 19 } 20 };
【234】Palindrome Linked List
【259】3Sum Smaller (2019年2月26日,谷歌 tag)
给了一个乱序的数组,能不能从中找出三个数,nums[i], nums[j], nums[k] 他们三个想加的和小于target,返回这样三个数的个数。要求时间复杂度是O(N^2)
题解:我想出来的 O(n^2) 的解法是用个map辅助,实时更新map。但是这题的标准解法是先sort,然后先选出一个数,然后剩下两个数用夹逼定理。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int threeSumSmaller(vector<int>& nums, int target) { 4 const int n = nums.size(); 5 sort(nums.begin(), nums.end()); 6 int res(0); 7 for (int i = 0; i < n; ++i) { 8 res += twoSumSmaller(nums, i + 1, target - nums[i]); 9 } 10 return res; 11 } 12 int twoSumSmaller(vector<int>& nums, int start, int target) { 13 int left = start, right = nums.size() - 1; 14 int res(0); 15 while (left < right) { 16 int sum = nums[left] + nums[right]; 17 if (sum < target) { 18 res += right - left; //包含nums[left]的所有可能解 19 ++left; 20 } else { 21 --right; 22 } 23 } 24 return res; 25 } 26 };
【283】Move Zeroes
【287】Find the Duplicate Number
【344】Reverse String (2018年12月3日,第一次review,ko)
逆序一个字符串。
题解:见string分类:https://www.cnblogs.com/zhangwanying/p/9885334.html
【345】Reverse Vowels of a String (2018年12月4日,第一次review,ko)
逆序一个字符串的元音字母。
题解:见string分类:https://www.cnblogs.com/zhangwanying/p/9885334.html
【349】Intersection of Two Arrays (2018年11月6日,算法群相关题)
hash-table 里面有这题,我就不重复写了。hash-table:https://www.cnblogs.com/zhangwanying/p/9886262.html
【350】Intersection of Two Arrays II (2018年11月6日,算法群)
hash-table 里面有这题,我就不重复写了。hash-table:https://www.cnblogs.com/zhangwanying/p/9886262.html
【360】Sort Transformed Array
Given a string that consists of only uppercase English letters, you can replace any letter in the string with another letter at most k times. Find the length of a longest substring containing all repeating letters you can get after performing the above operations.
Note:Both the string's length and k will not exceed 10^4.
Example 1: Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa. Example 2: Input: s = "AABABBA", k = 1 Output: 4 Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA". The substring "BBBB" has the longest repeating letters, which is 4.
题解:标准sliding window。但是那个cnt的意义有所不同,这里的cnt叫做 maxRepeat ,代表的意义有所不同。注意体会思路。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int characterReplacement(string s, int k) { 4 const int n = s.size(); 5 if (n == 0) {return 0;} 6 int start = 0, end = 0, maxRepeat = 0; 7 int res = 0; 8 unordered_map<char, int> mp; 9 for (; end < n; ++end) { 10 char c = s[end]; 11 mp[c]++; 12 maxRepeat = max(mp[c], maxRepeat); 13 while (end - start + 1 - maxRepeat > k) { 14 char t = s[start]; 15 mp[t]--; ++start; 16 for (auto& p : mp) { 17 maxRepeat = max(maxRepeat, p.second); 18 } 19 } 20 res = max(end - start + 1, res); 21 } 22 return res; 23 } 24 };
判断一个循环数组是不是里面是不是含有环,circular array.
If a number k at an index is positive, then move forward k steps. Conversely, if it’s negative (-k), move backward k steps. Since the array is circular, you may assume that the last element’s next element is the first element, and the first element’s previous element is the last element.
Determine if there is a loop (or a cycle) in nums. A cycle must start and end at the same index and the cycle’s length > 1. Furthermore, movements in a cycle must all follow a single direction. In other words, a cycle must not consist of both forward and backward movements.
Could you solve it in O(n) time complexity and O(1) extra space complexity?
题解:快慢指针。这个题目只能沿着一个方向走,所以我们需要判断一下,当前的方向是不是一开始的方向相同,也就是快指针每走一步都要判断一下出发点的数字是不是相同的符号,也就是while循环的部分。
当快慢指针相遇的时候,需要判断一下环的大小是不是1个结点,如果是的话这样是。如果大于一个结点,则返回true。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 bool circularArrayLoop(vector<int>& nums) { 4 const int n = nums.size(); 5 for (int i = 0; i < nums.size(); ++i) { 6 if (!nums[i]) {continue;} 7 int slow = i, fast = i; 8 while (nums[fast] * nums[i] > 0 && nums[getNext(fast, nums)] * nums[i] > 0) { 9 slow = getNext(slow, nums), fast = getNext(getNext(fast, nums), nums); 10 if (slow == fast) { 11 if (getNext(slow, nums) != slow) { return true; } 12 else {break;} 13 } 14 } 15 /* 16 //把走过的slow set成为不能走的状态 17 slow = i; 18 while (nums[slow] * nums[i] > 0) { 19 nums[slow] = 0; 20 slow = getNext(slow, nums); 21 } 22 */ 23 } 24 return false; 25 } 26 int getNext(int cur, vector<int>& nums) { 27 const int n = nums.size(); 28 int res = (nums[cur] + cur) % n; 29 return res < 0 ? res + n : res; 30 } 31 };
【487】Max Consecutive Ones II (2018年11月27日)(2019年3月3日更新)
给了一个0/1数组,问如果最多只能把一个 0 变成 1 的话,那么这个数组里面最长的连续 1 有几个?
题解:我是对于每一个 0 都求出了它前面连续 1 的个数 和后面连续 1 的个数。然后再遍历一边原数组,求长度,比较。注意如果全 1 的情况需要考虑。
discuss里面有更好的解法,不用多两个数组。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 int findMaxConsecutiveOnes(vector<int>& nums) {
4 const int n = nums.size();
5 vector<int> pre(n, -1), next(n, -1);
6 int cnt = 0;
7 for (int i = 0; i < n; ++i) {
8 if (nums[i] == 0) {
9 pre[i] = cnt;
10 cnt = 0;
11 } else {
12 cnt++;
13 }
14 }
15 cnt = 0;
16 for (int i = n-1; i >= 0; --i) {
17 if (nums[i] == 0) {
18 next[i] = cnt;
19 cnt = 0;
20 } else {
21 cnt++;
22 }
23 }
24 int ret = 0;
25 cnt = 0;
26 for (int i = 0; i < n; ++i) {
27 if (nums[i] == 0) {
28 ret = max(ret, pre[i] + next[i] + 1);
29 cnt = 0;
30 } else {
31 cnt++;
32 ret = max(cnt, ret);
33 }
34 }
35 return ret;
36 }
37 };
follow-up:What if the input numbers come in one by one as an infinite stream? In other words, you can't store all numbers coming from the stream as it's too large to hold in memory. Could you solve it efficiently?
2019年3月3日更新,这题和 1004 这周周赛题一模一样,这个是最多能变一个1,1004是是最多能变 k 个 1。解法是 sliding window with 2 pointers。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int findMaxConsecutiveOnes(vector<int>& nums) { 4 const int n = nums.size(); 5 int start = 0, cnt = 0, res = 0; 6 for (int i = 0; i < n; ++i) { 7 if (nums[i] == 0) {cnt++;} 8 while (cnt > 1) { 9 if (nums[start] == 0) {--cnt;} 10 ++start; 11 } 12 res = max(res, i - start + 1); 13 } 14 return res; 15 } 16 };
【524】Longest Word in Dictionary through Deleting
【532】K-diff Pairs in an Array
【567】Permutation in String
【632】Smallest Range
【713】Subarray Product Less Than K (2019年2月11日)
给了一个数组,返回有多少子数组的乘积小于K。
题解:暴力解法O(N^2),2 pointers + sliding window 可以优化到 O(N). 每一步都往移动一个end指针,如果当前的乘积大于等于k了,就往前移动begin指针。计算包含当前end的子数组个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int numSubarrayProductLessThanK(vector<int>& nums, int k) { 4 const int n = nums.size(); 5 int begin = 0, end = 0, multi = 1, res = 0; 6 while (end < n) { 7 multi = multi * nums[end]; 8 while (begin < end && multi >= k) { 9 multi /= nums[begin]; 10 ++begin; 11 } 12 if (multi < k) { 13 res += (end - begin + 1); 14 } 15 ++end; 16 } 17 return res; 18 } 19 };
【723】Candy Crush
【763】Partition Labels
【826】Most Profit Assigning Work
【828】Unique Letter String (H)(2019年3月5日)
A character is unique in string S
if it occurs exactly once in it.
For example, in string S = "LETTER"
, the only unique characters are "L"
and "R"
.
Let's define UNIQ(S)
as the number of unique characters in string S
.
For example, UNIQ("LETTER") = 2
.
Given a string S
with only uppercases, calculate the sum of UNIQ(substring)
over all non-empty substrings of S
.
If there are two or more equal substrings at different positions in S
, we consider them different.
Since the answer can be very large, return the answer modulo 10 ^ 9 + 7
.
Example 1: Input: "ABC" Output: 10 Explanation: All possible substrings are: "A","B","C","AB","BC" and "ABC". Evey substring is composed with only unique letters. Sum of lengths of all substring is 1 + 1 + 1 + 2 + 2 + 3 = 10 Example 2: Input: "ABA" Output: 8 Explanation: The same as example 1, except uni("ABA") = 1. Note: 0 <= S.length <= 10000.
题解:hanson solution: https://leetcode.com/problems/unique-letter-string/discuss/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 const int mod = 1e9+7; 4 int uniqueLetterString(string S) { 5 vector<vector<int>> dp(26, vector<int>(2, -1)); 6 int res = 0; 7 const int n = S.size(); 8 for (int i = 0; i < n; ++i) { 9 int idx = S[i] - 'A'; 10 res = (res + (dp[idx][1] - dp[idx][0]) * (i - dp[idx][1])) % mod; 11 dp[idx][0] = dp[idx][1]; 12 dp[idx][1] = i; 13 } 14 for (int i = 0; i < 26; ++i) { 15 res = (res + (dp[i][1] - dp[i][0]) * (n - dp[i][1])) % mod; 16 } 17 return res; 18 } 19 };
【838】Push Dominoes
【844】Backspace String Compare
【845】Longest Mountain in Array
【881】Boats to Save People