【专题一】双指针

129 阅读3分钟

双指针简介

分类:

  1. 对撞指针:一个从头开始,一个从尾部开始
  2. 快慢指针:追及问题

283. 移动零

题目描述

image.png

解题思路

  1. 另外开一个同样大小的数组,然后遍历原数组,将不为0的依次复制过去,最后面的就是0了
  2. 遍历两遍数组,第一遍统计0的个数并将非0元素排好位置,第二遍就是填充0
  3. 快排思想【划分区间 - 已处理部分,未处理部分】

image.png

代码实现

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. 复写零

题目描述

image.png

解题思路

  1. 开辟新数组,然后按照题目要求进行填充即可
  2. 双指针 --- 2.1 找到进行复写的最后一个元素位置 ---- 2.2 然后从后往前进行复写【从前往后不行,可画图查看】

image.png

代码实现

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. 盛最多水的容器

题目描述

image.png

解题思路

  1. 暴力【两层 for 循环】,找乘积最大的区间,会超时
  2. 对撞指针
    • 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. 有效三角形的个数

题目描述

image.png

解题思路

  1. 回溯思想【列举出所有的可能性,然后判断哪些满足条件】【三层 for 循环,3 * N * N *N】,排序优化:nlogn + N * N *N【判断处理上】
  2. 排序 + 二分:时间复杂度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]的】
  3. 排序 + 双指针:时间复杂度(O(N*N))

image.png

代码

// 二分
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;
}

下面题目见链接

juejin.cn/post/725888…

  1. 202. 快乐数
  2. 18. 四数之和
  3. 15. 三数之和