第三天的主题还是 two pointers,一道简单题,一道中等题。
283. Move Zeroes
Analysis
拿到这种题之后,脑子里面反应的第一解法,就是直接用 vector 拷贝...😂试了下,确实是可以解出来的。
不过既然出现在这里还是用双指针的方法做一下。
Code
method 1
1 | class Solution { |
method 2
1 | class Solution { |
method 3
按照上面代码的思路,从左边指针来考虑问题,会比较麻烦,实际上,可以直接从右边指针的角度来考虑问题。此时,左边指针就是非零数的尾部,右边指针就是待处理数字序列的头部,这样右边指针每遇到一个非零数,就直接移到左边。1
2
3
4
5
6
7
8
9
10
11
12
13class Solution {
public:
void moveZeroes(vector<int>& nums) {
int left = 0, right = 0, size = nums.size();
while(right < size) {
if(nums[right]) {
swap(nums[left], nums[right]);
left++;
}
right++;
}
}
};
167. Two Sum II - Input Array Is Sorted
Analysis
这个题是 1. Two Sum 的加强版,之前的解法在这里可以用。所以,首先想到的就是暴力求解跟哈希,但这两种方法都没有使用题目给的条件:数组有序。
Code
method 1
1 | /* method 1: violent solution, will cause time limit exceeded. */ |
暴力解法,果然不出所料的会超时啊。
method 2
1 | /* method 2: use hash map */ |
哈希虽然不超时,但是额外的消耗了内存空间。
method 3
考虑到数组有序,所以可以使用前两天学习的二分查找,这样时间复杂度就是 $O(nlogn)$ 了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/* method 3: use binary search */
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
for(int i = 0; i < numbers.size(); i++) {
int left = i + 1, right = numbers.size() - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(numbers[mid] == target - numbers[i]) {
return {i + 1, mid + 1};
} else if(numbers[mid] > target - numbers[i]) right = mid - 1;
else left = mid + 1;
}
}
return {-1, -1};
}
};
method 4
由于这个题出现在双指针里面,所以肯定可以使用双指针的解法。如何使用双指针呢?
需要设置两个指针分别从数组的左右两边进行遍历,每次将指针所指的值之和与目标值进行比较。如果大于目标值,那么左移右指针;如果小于,那么右移左指针。可能会有人觉得漏解,实际上并不会。从反证法的角度很容易想明白,如果右指针固定,左指针左边存在满足条件的解,或者左指针固定,右指针右边存在满足条件的解,那么这个数组一定不是有序的。1
2
3
4
5
6
7
8
9
10
11
12
13
14/* method 4: use two pointers */
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int left = 0, right = numbers.size() - 1;
while(left < right) {
int sum = numbers[left] + numbers[right];
if(sum < target) left++;
else if(sum > target) right++;
else return {left + 1, right + 1};
}
return {-1, -1};
}
};
Summary
双指针用来解决一些问题真是有奇效啊。大体上来讲,要么是从中间向两边遍历,要么是两边向中间遍历。遇到这种类型的题目的时候,不妨从这两个角度思考一下,说不定问题就能得到解决。