双指针之习题分析

一、两数之和——有序数组

(一)、题目需求

​ 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

​ 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

​ 说明:

​ 返回的下标值(index1 和 index2)不是从零开始的。
​ 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

(二)、解法

public class Solution {

    public static void main(String[] args) {
        System.out.println(new Solution().twoSum(new int[]{
                -1, 0
        }, -1));
    }

    public int[] twoSum(int[] numbers, int target) {
        if (numbers == null || numbers.length == 0) {
            return numbers;
        }
        int start = 0;
        int end = numbers.length - 1;
        int[] result = {-1, -1};
        while (start + 1 < end) {
            if ((numbers[start] + numbers[end]) == target) {
                result[0] = start + 1;
                result[1] = end + 1;
                break;
            } else if ((numbers[start] + numbers[end]) > target) {
                end -= 1;
            } else {
                start += 1;
            }
        }
        return result;
    }
}

(三)、代码分析

​ 结束指针开始位置指向数组的最后一位;开始指针开始位置指向数组的第一位。

​ 进行开始指针以及结束指针指向的值的判断:

​ 1、情况1:开始指针与结束指针指向的值相加刚好等于目标值,直接退出循环。

​ 2、情况2:开始指针与结束指针指向的值相加大于目标值,由于数组为有序数组,所以将结束指针前移一位。

​ 3、情况3:开始指针与结束指针指向的值相加小于目标值,由于数组为有序数组,所以将开始指针后移一位。

while (start + 1 < end) {
    if ((numbers[start] + numbers[end]) == target) {
        break;
    } else if ((numbers[start] + numbers[end]) > target) {
        end -= 1;
    }else {
        start +=1;
    }
}

(四)、流程图

1、初始状态,输入数组以及目标值
在这里插入图片描述
2、设置开始指针和结束指针
在这里插入图片描述
3、相加大于目标值,结束指针前移一位
在这里插入图片描述
4、相加等于目标值,退出循环
在这里插入图片描述

二、三数之和

(一)、题目需求

​ 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

​ 注意:答案中不可以包含重复的三元组。

​ 示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

(二)、解法

public class Solution {

    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return result;
        }
        Arrays.sort(nums);
        int len = nums.length;
        for (int i = 0; i < len - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int start = i + 1;
            int end = len - 1;
            while (start < end) {
                if (nums[i] + nums[start] + nums[end] == 0) {
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(nums[i]);
                    list.add(nums[start]);
                    list.add(nums[end]);
                    start++;
                    end--;
                    result.add(list);
                    while (start < end && nums[start] == nums[start - 1]) {
                        start++;
                    }
                    while (start < end && nums[end] == nums[end + 1]) {
                        end--;
                    }
                } else if (nums[i] + nums[start] + nums[end] > 0) {
                    end--;
                } else {
                    start++;
                }
            }
        }
        return result;
    }
}

(三)、代码分析

1、由于输入数组为无序数组,首先将该数组进行排序操作。

Arrays.sort(nums);

2、进行for循环判断,设立“i”只到达len-2,预留后面两个位置给其余两个数字。同时进行判断,假如当前“i”的值与前一个值相同,则说明情况相同,无需进行重复操作,直接跳出该层循环。

for (int i = 0; i < len - 2; i++) {
    if (i > 0 && nums[i] == nums[i - 1]) {
        continue;
    }
    // ......
}

3、设置start指针和end指针:start取i的后一位,end取当前数组的最后一位。

int start = i + 1;
int end = len - 1;

4、进行判断

  • 情况1:当i对应的值加上开始指针指向的值以及结束指针指向的值等于0时,创建List集合,将其加入结果集中。并将开始指针后移一位,结束指针前移一位。
    • 假如开始指针移动后,数值与移动前相同,则继续后移
    • 假如结束指针移动后,数值与移动前相同,则继续前移
  • 情况2:当i对应的值加上开始指针指向的值以及结束指针指向的值大于0时,说明总和过大,则需要减小该和,因此将结束指针前移一位。
  • 情况3:当i对应的值加上开始指针指向的值以及结束指针指向的值小于0时,说明总和过小,则需要增大该和,因此将开始指针后移一位。
while (start < end) {
    if (nums[i] + nums[start] + nums[end] == 0) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(nums[i]);
        list.add(nums[start]);
        list.add(nums[end]);
        start++;
        end--;
        result.add(list);
        while (start < end && nums[start] == nums[start - 1]) {
            start++;
        }
        while (start < end && nums[end] == nums[end + 1]) {
            end--;
        }
    } else if (nums[i] + nums[start] + nums[end] > 0) {
        end--;
    } else {
        start++;
    }
}

(四)、流程图

1、初始状态
在这里插入图片描述
2、进行数组排序
在这里插入图片描述
3、开始for循环,并设置start指针与end指针。

在这里插入图片描述
4、开始指针与结束指针相加小于0,因此开始指针后移一位
在这里插入图片描述
5、不断进行判断,由于3个数值相加一直小于0,所以开始指针移动至结束指针前一位。再进行一次判断仍小于0,start指针将移动至结束指针位置,此时第一层循环结束
在这里插入图片描述
6、第二层循环开启:i后移一位,同时重新设置开始指针与结束指针
在这里插入图片描述
7、3个数值相加等于0因此,开始指针后移一位,结束指针前移一位。第二次判断,仍等于0,此时start指针将后移一位,结束指针将前移一位,即两者交换了指向的位置,此时开始指针大于结束指针,所以第二层循环结束。
在这里插入图片描述
8、开启第三层循环:i+1,同时重新设置开始指针与结束指针。由于i+1后,所对应的值与前一位相同,所以无需重复判断,直接退出第三层循环。
在这里插入图片描述
9、开启第4层循环,i+1,同时重新设置开始指针与结束指针。由于3个数值相加大于0,所以结束指针将前移一位,此时开始指针与结束指针将相等,所以第四层循环退出。for循环结束。
在这里插入图片描述

三、验证三角形

(一)、题目需求

​ 给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。

​ 示例 1:

输入: [2,2,3,4]
输出: 3
解释:
有效的组合是: 
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

(二)、解法

public class Solution {

    public static void main(String[] args) {
        System.out.println(new Solution().triangleNumber(new int[]{
                1, 2, 3, 4, 5, 6
        }));
    }

    public int triangleNumber(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        Arrays.sort(nums);
        int total = 0;
        for (int i = nums.length - 1; i >= 2; i--) {
            int start = 0;
            int end = i - 1;
            while (start < end) {
                if (nums[start] + nums[end] > nums[i]) {
                    total += (end - start);
                    end--;
                } else {
                    start++;
                }
            }
        }
        return total;
    }
}

(三)、代码分析

1、排序数组

Arrays.sort(nums);

2、开始for循环,并将i初始值设为数组的最后一位,结束值为第三位,预留两位数值作为三角形的另外两条边的判断。并设置开始指针与结束指针

for (int i = nums.length - 1; i >= 2; i--) {
    int start = 0;
    int end = i - 1;
    // ......
}

3、进行判断:

  • 情况1:开始指针与结束指针相加大于i对应的值,由于数组已进行了排序操作。因此假如开始指针与结束指针相加已大于i对应的值,则开始指针至结束指针中间的值皆大于等于开始指针,因此中间的值与结束指针相加同样大于i,因此三角形的数量需加上结束指针与开始指针的差。
  • 情况2:开始指针与结束指针相加小于i对应的值,由于数组已进行了排序操作。所以需要增大开始指针与结束指针的和,因此增大开始指针。
while (start < end) {
    if (nums[start] + nums[end] > nums[i]) {
        total += (end - start);
        end--;
    } else {
        start++;
    }
}

(四)、流程图

1、初始状态,并进行数组排序
在这里插入图片描述
2、开启for循环,start指针对应的值加end指针对应的值大于i对应的值,因此三角形数量加上结束指针与开始指针的差,同时结束指针前移一位
在这里插入图片描述
3、start指针对应的值加end指针对应的值等于i对应的值,因此开始指针后移一位
在这里插入图片描述
4、start指针等于结束指针,退出while循环,第一层for循环结束

在这里插入图片描述
5、开启第二轮for循环,i前移一位,重新设置start指针与end指针。start指针对应的值加end指针对应的值大于i对应的值,因此三角形数量加上结束指针与开始指针的差,同时结束指针前移一位
在这里插入图片描述
6、start指针等于结束指针,退出while循环,此时i已经等于数组的第三位数,所以for循环结束
在这里插入图片描述

四、存水问题

(一)、题目需求

​ 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

​ 示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

​ 示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

(二)、解法

public class Solution {

    public static void main(String[] args) {
        System.out.println(new Solution().trap(new int[]{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}));
    }

    public int trap(int[] height) {
        if (height == null || height.length == 0) {
            return 0;
        }
        int start = 0;
        int end = height.length - 1;
        int startHeight = height[start];
        int endHeight = height[end];
        int total = 0;
        while (start < end) {
            if (startHeight <= endHeight) {
                if (startHeight > height[start + 1]) {
                    total += startHeight - height[start + 1];
                } else {
                    startHeight = height[start + 1];
                }
                start++;
            } else {
                if (endHeight > height[end - 1]) {
                    total += endHeight - height[end - 1];
                } else {
                    endHeight = height[end - 1];
                }
                end--;
            }
        }
        return total;
    }
}

(三)、代码分析

1、初始化开始指针、结束指针、开始的最高高度,结束的最高高度。

int start = 0;
int end = height.length - 1;
int startHeight = height[start];
int endHeight = height[end];
int total = 0;

2、进行判断:

  • 情况1:开始高度小于结束高度:
    • 假如开始高度大于start指针指向的下一位对应的高度,则说明start指针指向的下一位格子可以存储的水量为开始高度减去该格的高度。
    • 假如开始高度小于start指针指向的下一位对应的高度,则说明开始高度需要更新,因此更新开始高度
  • 情况2:开始高度大于等于结束高度:
    • 假如结束高度大于end指针指向的上一位对应的高度,则说明end指针指向的上一位格子可以存储的水量为结束高度减去该格的高度。
    • 假如结束高度小于end指针指向的上一位对应的高度,则说明结束高度需要更新,因此更新结束高度
while (start < end) {
    if (startHeight <= endHeight) {
        if (startHeight > height[start + 1]) {
            total += startHeight - height[start + 1];
        } else {
            startHeight = height[start + 1];
        }
        start++;
    } else {
        if (endHeight > height[end - 1]) {
            total += endHeight - height[end - 1];
        } else {
            endHeight = height[end - 1];
        }
        end--;
    }
}

(四)、流程图分析

1、初始状态
在这里插入图片描述
2、设置开始指针,结束指针,开始高度,结束高度

在这里插入图片描述
3、开始高度小于结束高度,因此结束指针前移一位,同时更新开始高度
在这里插入图片描述
4、开始指针指向的值的下一个值小于开始高度,因此将结果+1,同时开始指针后移
在这里插入图片描述
5、开始指针指向的值的下一个大于开始高度,因此更新开始高度,同时开始指针后移
在这里插入图片描述
6、结束指针小于开始指针,结束指针前移,同时结束高度进行更新
在这里插入图片描述
7、开始指针不断后移,total不断进行相加,直到开始指针指向的下一个值大于开始高度,更新开始高度,同时开始指针后移一位
在这里插入图片描述
8、结束指针前移,同时total进行运算,直至结束指针位于开始指针的后一位,退出循环

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值