双指针问题2


1. 有效三角形的个数(611)

题目描述:
在这里插入图片描述

算法原理:
这题使用的方法要进行理解首先要知道一个比较基础的数学知识就是三角形的三条边当中只要最短的那两条边的和大于第三条边那么这三条边就是可以构成三角形的,具体原因就不阐述了其实也很简单。
知道了这个数学知识我们首先要做的就是将数组进行升序排序,以此来方便我们后续进行操作。我们从数组的最后一个元素开始遍历到数组的2下标的元素,这个元素位置记为i,事实上可以使用for循环完成这个遍历。然后对于i位置元素的左边的数组元素进行分析,在for循环内部定义left指针指向0位置,right指针指向i-1位置。因为我们将nums[i]的值视为第三条边的长度,nums[left]和nums[right]视为第一条边和第二条边的长度,那么此时分为两种情况。
第一种情况就是nums[left]+nums[right]>nums[i]那么此时是可以构成三角形的,因为nums[left]加上nums[right]都可以大于nums[i],显然在nums数组的left+1~right-1下标的元素加上nums[right]都会大于nums[i],也就是可以构成三角形,所以我们无需去遍历这个区间内的元素就可以直接得到在固定三角形第二条边和第三条边长度的情况下有多少种不同可以构成三角形的三元组,最终将right–,并且将最终返回的ret也就是返回的可以构成三角形的三元组的个数加上right-left。
第二种情况就是nums[left]+nums[right]<=nums[i]那么此时是不可以构成三角形的,因为此时连nums[right]加上nums[left]都已经小于nums[i],显然在nums数组的left+1~right-1下标的元素加上nums[left]都会小于nums[i],也就是不可以构成三角形,所以也是一样我们无需去遍历这个区间内的元素直接跳过即可,最终将left++。
重复以上两种情况的判断,直至right和left相遇,这相当于在开始的for循环中再去写一个循环去处理nums数组的0~i-1这个区间,具体逻辑如代码所示。
代码如下:

class Solution {
    public int triangleNumber(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        int ret = 0;
        for (int i = n - 1; i >= 2; i--) {
            int left = 0;
            int right = i - 1;
            while (left < right) {
                int temp = nums[left] + nums[right];
                if (temp > nums[i]) {
                    ret += right - left;
                    right--;
                } else {
                    left++;
                }
            }
        }
        return ret;
    }
}

题目链接

2. 查找总价格为目标值的两个商品(LCR179)

题目描述:
在这里插入图片描述

算法原理:
其实这一题是可以使用二分查找直接做出来的,但是在学习一种方法的时候就要尽量多的去使用它,从而逐渐熟练,因此这里使用双指针的方法。跟上题使用相似的思想,就是定义left和right指针来不断缩小区间范围,满足条件跳出循环然后返回一种结果即可。
代码如下:

class Solution {
    public int[] twoSum(int[] price, int target) {
        int right = price.length - 1;
        int left = 0;

        while (left < right) {
            if (price[left] + price[right] > target) {
                right--;
            } else if (price[left] + price[right] < target) {
                left++;
            } else {
                break;
            }
        }

        return new int[] { price[left], price[right] };
    }
}

题目链接

3. 三数之和(15)

题目描述:
在这里插入图片描述

算法原理:
这一题也是和前面的思想类似,不过这里需要先给数组排序得到升序的数组,然后固定住第三个数nums[i](这里的i可以取2到nums.length-1,2是因为至少要留两个值作为前两个数),在0到i-1这个区间内去找到两个值满足nums[left]+nums[right]=-nums[i],这一题就转化为和第一题一致的题目,就可以使用做第一题使用的方法甚至说代码都可以直接套用。但是不同的一点在于,第一题得到的三元组可以重复,但是这一题不可以,所以我们要进行去重,在找到符合条件的三元组后left和right指针移动之后要判断该位置元素和前一个是否相同,如果相同就直接跳过不同就可以继续进行处理,当然这里的跳过过程要注意越界的问题。我们对于固定的第三个数也要进行去重,道理也是一样,就是i进行移动后,要判断当前位置元素和前一个是否相同,如果相同直接跳过,这样就完成了去重。
代码如下:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        List<List<Integer>> retList = new ArrayList<>();

        for (int i = n - 1; i >= 2; i--) {
            int left = 0;
            int right = i - 1;
            while (right > left) {
                int sum = nums[left] + nums[right];
                if (sum > -nums[i]) {
                    right--;
                } else if (sum < -nums[i]) {
                    left++;
                } else {
                    retList.add(new ArrayList(Arrays.asList(nums[left], nums[right], nums[i])));
                    left++;
                    right--;
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while (right > left && nums[right] == nums[right + 1]) {
                        right--;
                    }
                }
            }
            while (i > 1 && nums[i] == nums[i-1]) {
                i--;
            }
        }
        return retList;
    }
}

题目链接

4. 四数之和(18)

题目描述:
在这里插入图片描述

算法原理:
这题和上一题三数之和就是一致的,但是就是这里要多套一层循环去多固定一个数,也就是通过两层循环固定两个数分别为nums[i]和nums[j],然后根据题意nums[left]+nums[right]需要满足target-nums[i]-nums[j]这样的条件。另外就是也需要去重,相比三数之和就是多对一层循环去一次重。
代码如下:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new ArrayList();
        Arrays.sort(nums);
        int n = nums.length;
        for (int i = n - 1; i > 2;) {
            for (int j = i - 1; j > 1;) {
                int left = 0;
                int right = j - 1;
                long aim =  (long)target - nums[i] - nums[j];
                while (left < right) {
                    int temp = nums[left] + nums[right];
                    if (temp > aim) {
                        right--;
                    } else if (temp < aim) {
                        left++;
                    } else {
                        list.add(new ArrayList(Arrays.asList(nums[left], nums[right], nums[j], nums[i])));
                        left++;
                        right--;
                        while (left < right && nums[left - 1] == nums[left]) {
                            left++;
                        }
                        while (left < right & nums[right + 1] == nums[right]) {
                            right--;
                        }
                    }
                }
                j--;
                while (j > 1 && nums[j + 1] == nums[j]) {
                    j--;
                }
            }
            i--;
            while (i > 2 && nums[i + 1] == nums[i]) {
                i--;
            }
        }
        return list;
    }
}

题目链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值