一、原理
在处理一个两侧元素数值较大或较小的数组、寻找盛水最多的容器等情况下。可以使用两个指针(左指针left和右指针right)解决问题。其中左指针指向数组头元素,右指针指向数组末位元素,两个指针在一定条件下可以相向移动,最终遍历整个数组。由于仅进行一次遍历,该方法时间复杂度为O(n),优势明显。
二、限制条件
- 操作的数据类型为数组
- 其中的元素数值大小按照两侧高(低),中间低(高)的规律排布
- 结果中要求找出两侧元素的柱状图所围成的“水槽容量”的极值
三、时空复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(n)或O(1)
四、常见题型
1.有序数组的平方
题目出处:977. 有序数组的平方 - 力扣(LeetCode)
解题思路:
由于原有序数组中可能存在负数,所以新数组不一定是有序数组。在将数组中所有元素平方(时间复杂度为O(n))以后,若直接使用库函数sort()(时间复杂度为O(nlogn))完成新数组的排序,将会增大算法的时间复杂度。对撞指针法解题思路如下:
- 对于m,n<0,若m<n,则恒有m*m>n*n。求平方以后新数组中的元素两侧数值大,中间数值小。
- 新建一个大小与原数组相同的数组用于存储答案
- 设置两个指针分别指向原数组两端,不断循环比较左右指针所指元素的大小
- 若左(右)指针元素的值较大,则将其元素按照从右往左的顺序存入新数组
- 将左(右)指针向右(左)移动一位,直到两指针会合
代码实例:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int len = nums.size();
int n = len - 1; // n从右向左遍历新数组以便完成插入操作
int left = 0;
int right = len - 1;
vector<int> res(n + 1, 0);
// 求数组元素的平方数
for(int i = 0; i < len; ++i) {
nums[i] = nums[i]*nums[i];
}
while(n >= 0) {
if(nums[left] >= nums[right]) {
res[n--] = nums[left++];
}else {
res[n--] = nums[right--];
}
}
return res;
}
};
注意事项:
- while()判断条件不可使用n > 0,否则将导致新数组首个元素未被处理
- if(nums[left] >= nums[right]) 中需要使用大于等于号或小于等于号,保证当两指针指向的元素值相同或两指针会合时,仍可以返回正确元素值
2.盛最多水的容器
题目出处:11. 盛最多水的容器 - 力扣(LeetCode)
![原题图](https://img-blog.csdnimg.cn/93193fafd83645d2a8fc66d5ede62cd3.png)
解题思路:
本题如使用暴力求解则时间复杂度为O(n*n),要想将其优化为O(n),需要运用缩减搜索空间的思想。(下文以示例1为例进行分析)
- 注意水体容量由较短的柱子和两元素相对位置共同决定。为了使相对位置最大,两指针从左右两端开始检查。也即分析最左边的 0 号和最右边的 7 号(此处记为(0,7))计算它们的容量。
- 然后比较两根柱子的高度,从其中较短的一根(0号)入手寻找更优解,因为水体高度由较短的一根柱子决定,当前水体容量受到该柱子的高度限制。此时无论右侧柱子有多高,都不可能使面积增大,同时由于其他柱子与0号柱的相对位置较小,如果移动7号柱,水的面积必然缩小,由此我们可以排除(0,6)、(0,5)、(0,4)、(0,3)、(0,2)、(0,1)从而达到缩减搜索空间的目的。
- 判断0号为限制因素后,记录下此时的水体面积,左指针右移一位。比较后发现height[1] > height[7]。7号位成为限制因素,面积有可能增大,记录此时面积并比较取较大值。再将右指针左移一位。此时我们又排除了(2,7)、(3,7)、(4,7)、(5,7)、(6,7)。
- 重复该步骤,直到两指针相遇,即完成查找
代码实例:
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0;
int right = height.size() - 1;
int res = 0;
int temp = 0;
while(right > left) {
if(height[left] <= height[right]) {
temp = (right - left)*height[left++];
if(temp > res) res = temp;
}else {
temp = (right - left)*height[right--];
if(temp > res) res = temp;
}
}
return res;
}
};