目录
1. LeetCode 283. 移动零
1.1 题目描述
1.2 题目思路
1.3 实现代码
2. LeetCode 1089. 复写零
2.1 题目描述
2.2 题目思路
2.3 实现代码
3. LeetCode 202. 快乐数
3.1 题目描述
3.2 题目思路
3.3 实现代码
4. LeetCode 11. 盛水最多的容器
4.1 题目描述
4.2 题目思路
4.3 实现代码
5. LeetCode 611. 有效三角形的个数
5.1 题目描述
5.2 题目思路
5.3 实现代码
6. LeetCode LCR 179.查找总价格为目标值的两个商品
6.1 题目描述
6.2 题目思路
6.3 实现代码
7. LeetCode 15. 三数之和
7.1 题目描述
7.2 题目思路
7.3 实现代码
8. LeetCode 18. 四数之和
8.1 题目描述
8.2 题目思路
8.3 实现代码
9. 总结
1. LeetCode 283. 移动零
1.1 题目描述
题目链接:
LeetCode 283. 移动零https://leetcode.cn/problems/move-zeroes/description/
1.2 题目思路
题目要求,将数字零移动到数组最后,并不影响非零元素的相对位置,
- 我们可以利用两个指针dest和cur,cur用来遍历数组,dest用来指向非零元素的最后一个位置;
- 要保证[0, dest] 的元素是非零元素,[dest + 1, cur - 1] 的元素是全零,[cur , size() - 1]是未处理过的元素;
- 那我们只需看一下cur指向的元素是否是零,如果是零,就++cur,否则就交换dest和cur所在位置的值即可,然后再++cur,++dest。
1.3 实现代码
class Solution
{
public:void moveZeroes(vector<int>& nums) {int dest = -1, cur = 0;while(cur < nums.size()){if (nums[cur] != 0) {++dest;swap(nums[cur],nums[dest]);}++cur;}}
};
2. LeetCode 1089. 复写零
2.1 题目描述
题目链接:
LeetCode 1089. 复写零https://leetcode.cn/problems/duplicate-zeros/
2.2 题目思路
这题依然是定义两个指针,cur和dest,cur用来遍历数组,如果dest从前往后覆盖,肯定会把还未处理的值覆盖,所以dest只能从后往前覆盖,那我们肯定要找到最后一个要覆盖的值的位置,那如何找到呢:
- 如果cur所在位置的值为零,那dest就往后走两步;
- 否则dest往后走一步,每次走完都要判断dest所在的位置是否大于了size();
- 然后再++cur;
- 最后cur所在位置就是最后一个要复写的值。
这里需要有一个细节判断,当最后一个要复写的位置是零,并且如果dest向后走两步的话就超过了size(),这个时候应该:
- --dest,然后让dest所在位置的值赋为0;
- --cur。
OK,这样就可以从后往前开始复写了:
- 如果cur所在位置的值不为零,就把这个位置的值赋给dest所在的位置;
- 然后--cur,--dest,直到cur < 0。
2.3 实现代码
class Solution
{
public:void duplicateZeros(vector<int>& arr) {int cur = 0, dest = -1;// 找到最后一个数while (cur < arr.size()){if (arr[cur] != 0) { ++dest;}else{dest += 2;}if (dest >= arr.size() - 1) break;++cur;}// 判断边界if (dest == arr.size()){--dest;arr[dest] = 0;--dest;--cur;}// 从后往前复写while (cur >= 0){if (arr[cur]){arr[dest] = arr[cur];--dest; --cur;}else{arr[dest - 1] = arr[dest] = arr[cur];--cur; dest -= 2;}}}
};
3. LeetCode 202. 快乐数
3.1 题目描述
LeetCode 202. 快乐数https://leetcode.cn/problems/happy-number/
3.2 题目思路
对于这道数字变换的题目,怎么能用到双指针呢?
其实这里的双指针,并不是实际意义上的指针,听我细细道来:
- 这道题有一个点非常重要,题目说经过不停地变换,最后要么是无限循环,要么是变为1,其实变为1之后也是无限循环;
- 那我么就可以运用这个规律来实现,定义一个slow,每次变换一次,fast,每次变换两次,那么最后fast和slow肯定相等,相等之后退出循环;
- 最后如果slow和fast变成了1,那么就是快乐书,否则不是。
3.3 实现代码
class Solution
{
public:int changeNum(int n){int sum = 0;while (n){sum += (n % 10) * (n % 10);n /= 10;}return sum;}bool isHappy(int n) {int slow = n, fast = changeNum(n);while (slow != fast){slow = changeNum(slow);fast = changeNum(fast);fast = changeNum(fast);}if (slow == 1) return true;return false;}
};
4. LeetCode 11. 盛水最多的容器
4.1 题目描述
题目链接
11. 盛最多水的容器 - 力扣(LeetCode)https://leetcode.cn/problems/container-with-most-water/description/
4.2 题目思路
方法一:
这题暴力解法思路很简单,就是把每个面积都求出来,取最小的就行,很明显时间复杂度是O(N^2),就不多讲了。
方法二:
- 定义两个指针left和right,left指向数组的开头位置,right指向数组的最后一个元素位置;
- 算出此时的面积square;
- 因为square = height * width,他的高度是由较小的那个决定的,如果left所在位置的值小,如果right向前走,height不会变,但是width肯定会变小,所以面积肯定是会变小的,此时就不用多余的计算面积了,直接让left--;
- 同理如果right所在位置的值小,那么如果让left++,那么height不会变,但是width肯定会变小,所以面积肯定会减小,这个时候--right即可。
- 直至left与right相遇。
那么这个算法的时间复杂度是left和right便利了一边数组,时间复杂度是O(N);
4.3 实现代码
class Solution
{
public:int _Square(int width, int height){return width * height;}int maxArea(vector<int>& height) {int left = 0;int right = height.size() - 1;int Square = 0;while (left < right){int tmpSquare = _Square(right - left, min(height[right],height[left]));if (tmpSquare > Square) Square = tmpSquare;if (height[right] < height[left]) --right;else ++left;}return Square;}
};
5. LeetCode 611. 有效三角形的个数
5.1 题目描述
题目链接
611. 有效三角形的个数 - 力扣(LeetCode)https://leetcode.cn/problems/valid-triangle-number/
5.2 题目思路
方法一:
这题暴力解法就是把数组中取三个数的情况都列举出来,并判断是否能构成三角形,但是三角形要满足任意两边之和大于第三边,因此要判断三次,但是我们只要知道最大的边是哪一条,就可以判断一次,因此排序可以简化判断次数,但是这样的时间复杂度还是很高。
方法二:
既然数组必然要进行排序,那么数组肯定是有序了,既然有序了,我们可以思考一下更简单的方法。
- 既然要判断两个较小值的和是否大于最大的数,我们可以从后向前固定最大数;
- 然后定义left = 0,right为最大数位置的前一个位置;
- 判断两个位置的值是否大于最大的数,如果大于,那么说明[left, right - 1]区间内所有的值都能与 right 和 固定的最大数组成三角形,然后--right;
- 如果不大于,就让left++;
- 直至left与right相遇。
5.3 实现代码
class Solution
{
public:int triangleNumber(vector<int>& nums) {int ret = 0;int n = nums.size();int loc = n - 1;sort(nums.begin(),nums.end());while (loc > 0){int left = 0, right = loc - 1;while (left < right){// 判断较小的两边之和是否大于最大边if (nums[left] + nums[right] > nums[loc]){ret += (right - left);--right;}else{++left;}}--loc;}return ret;}
};
6. LeetCode LCR 179.查找总价格为目标值的两个商品
6.1 题目描述
题目链接
LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/description/
6.2 题目思路
这题暴力解法思路也很简单,就是计算所有的两数之和即可。很明显时间复杂度O(N^2)。
除此之外,我们可以利用排序+单调性来做:
- 首先对数组进行排序;
- 定义两个指针left和right分别指向第一个元素和最后一个元素所在的位置;
- 判断此时两个位置的和是否==target;
- 如果大于就--right,小于就++left,等于就返回。
6.3 实现代码
class Solution
{
public:vector<int> twoSum(vector<int>& price, int target) {sort(price.begin(),price.end());int left = 0, right = price.size() - 1;while (left < right){if (price[left] + price[right] > target) --right;else if (price[left] + price[right] < target) ++left;else return {price[left],price[right]};}return {0,0};}
};
7. LeetCode 15. 三数之和
7.1 题目描述
题目链接
15. 三数之和 - 力扣(LeetCode)https://leetcode.cn/problems/3sum/description/
7.2 题目思路
这题与两数之和较为类似,与两数之和稍微不同的是,题⽬中要求找到所有「不重复」的三元组。那我们可以利⽤在两数之和
那⾥⽤的双指针思想,来对我们的暴力枚举做优化:
- 先排序;
- 然后固定⼀个数a :
- 在这个数后⾯的区间内,使⽤「双指针算法」快速找到两个数之和等于a即可。
但是要注意的是,这道题⾥⾯需要有「去重」操作
- 找到⼀个结果之后, left 和right指针要「跳过重复」的元素;
- 当使⽤完⼀次双指针算法之后,固定的 a 也要「跳过重复」的元素
7.3 实现代码
class Solution
{
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> vv;sort(nums.begin(),nums.end());for (int i = 0;i <= nums.size() - 3; ++i){if (nums[i] > 0){break;}int left = i + 1;int right = nums.size() - 1;while (left < right){if (nums[left] + nums[right] + nums[i] < 0) ++left;else if (nums[left] + nums[right] + nums[i] > 0) --right;else {vv.push_back({nums[left],nums[right],nums[i]});while (left < right && nums[left] == nums[left + 1]) {++left;}++left;while (left < right && nums[right] == nums[right - 1]) {--right;}--right;}}while (i < nums.size() - 3 && nums[i] == nums[i + 1]){++i;}}return vv;}
};
8. LeetCode 18. 四数之和
8.1 题目描述
题目链接
18. 四数之和 - 力扣(LeetCode)https://leetcode.cn/problems/4sum/description/
8.2 题目思路
这题和三数之和也很类似,首先固定一个数,然后利用三数之和的思想。
8.3 实现代码
class Solution
{
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> vv;sort(nums.begin(), nums.end());if (nums.size() < 4) return vv;for (int i = 0;i <= nums.size() - 4; ++i){for (int j = i + 1; j <= nums.size() - 3; ++j){int left = j + 1, right = nums.size() - 1;while (left < right){if ((long long)nums[i] + nums[j] + nums[left] + nums[right] < target){++left;}else if ((long long)nums[i] + nums[j] + nums[left] + nums[right] > target){--right;}else{vv.push_back({nums[i],nums[j],nums[left],nums[right]});while (left < right && nums[left] == nums[left + 1]){++left;}++left;while (left < right && nums[right] == nums[right - 1]){--right;}--right;}}while (j < nums.size() - 3 && nums[j] == nums[j + 1]){++j;}}while (i < nums.size() - 4 && nums[i] == nums[i + 1]){++i;}}return vv;}
};
9. 总结
总的来说,在双指针中,经常利用单调性来解决问题,例如这里的 盛最多水的容器、有效三角形的个数、包括两数之和、三数之和、四数之和,还需要多练习来锻炼思维,加油!