双指针简介
分类:
- 对撞指针:一个从头开始,一个从尾部开始
- 快慢指针:追及问题
283. 移动零
题目描述
解题思路
- 另外开一个同样大小的数组,然后遍历原数组,将不为0的依次复制过去,最后面的就是0了
- 遍历两遍数组,第一遍统计0的个数并将非0元素排好位置,第二遍就是填充0
- 快排思想【划分区间 - 已处理部分,未处理部分】
代码实现
public void moveZeroes(int[] nums) {
// left 非0元素的最后一个下标
int left = -1;
int right = 0;
while (right < nums.length) {
if(nums[right] != 0) {
// left+1的位置放该非0元素
left++;
// 交换两个元素,虽然大多数情况下,交换的都是非0与0交换,但是不能直接将left进行填充,然后将right替换为0,特例:数组只有一个非0元素
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
// 不能直接这样,如果只有一个不为0的元素的话,就会出问题
// nums[right] = 0;
}
right++;
}
}
1089. 复写零
题目描述
解题思路
- 开辟新数组,然后按照题目要求进行填充即可
- 双指针 --- 2.1 找到进行复写的最后一个元素位置 ---- 2.2 然后从后往前进行复写【从前往后不行,可画图查看】
代码实现
public void duplicateZeros(int[] arr) {
// 1. 先找到最后一个复写的数
int dest = -1,cur = 0;
int n = arr.length;
while (dest < n) {
if(arr[cur] != 0) {
dest++;
} else {
dest+=2;
}
if(dest >= n-1) break;
cur++;
}
// 1.5 处理边界
if(dest == n) {
arr[n-1] = 0;
cur--;
dest -= 2;
}
// 2. 从后往前进行复写
while (cur >= 0) {
if(arr[cur] == 0) {
arr[dest--] = 0;
arr[dest--] = 0;
cur--;
} else {
arr[dest--] = arr[cur--];
}
}
}
11. 盛最多水的容器
题目描述
解题思路
- 暴力【两层 for 循环】,找乘积最大的区间,会超时
- 对撞指针
- left 在左,right 在右,计算容积
- 木桶效应,left 和 right 较短的哪个往中间走一步
- 直到left right 相遇即可结束
代码
public int maxArea(int[] height) {
int left = 0;
int right = height.length-1;
int max = 0;
while(left < right) {
int tmp = Math.min(height[left],height[right]) * (right - left);
max = Math.max(tmp,max);
if(height[left] < height[right]) {
left++;
} else {
right--;
}
}
return max;
}
611. 有效三角形的个数
题目描述
解题思路
- 回溯思想【列举出所有的可能性,然后判断哪些满足条件】【三层 for 循环,3 * N * N *N】,排序优化:nlogn + N * N *N【判断处理上】
- 排序 + 二分:时间复杂度O(logN * N * N) 【外面两个for循环,第三层使用二分,找到最大满足nums[i] + nums[j] > nums[k] 的值,就是 k 的最大值,然后直接加个数 k - j,防止nums[i] == 0,所以 k 的初始值为 k = j ==> [j+1,n-1] 这个区间的数肯定都大于 num[j]的】
- 排序 + 双指针:时间复杂度(O(N*N))
代码
// 二分
public int triangleNumber(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
int ans = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
int left = j + 1, right = n - 1, k = j;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] < nums[i] + nums[j]) {
k = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
ans += k - j;
}
}
return ans;
}
// 3. 双指针
public int triangleNumber(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
int count = 0;
for(int i = n-1; i>=0; i--) {
// 利用双指针
int left = 0;
// 一开始 right 的位置为倒数第二个值
int right = i - 1;
while(left < right) {
if(nums[left] + nums[right] > nums[i]) {
count += (right - left);
right--;
} else {
left++;
}
}
}
return count;
}