算法——双指针之快乐数、盛水最多的容器、有效三角形的个数

26 篇文章 0 订阅
文章讨论了如何利用双指针技巧和优化策略解决LeetCode中的三个问题:判断快乐数是否循环、计算盛水最多的容器体积以及高效计数有效三角形。通过实例分析和代码示例,展示了如何通过排序和减少不必要的比较来提升算法效率。
摘要由CSDN通过智能技术生成

3.快乐数

题目:. - 力扣(LeetCode)

本题说明只有两种情况,即最后会出现1重复,或者也可能是无限循环但始终变不到 1。我们实际上可以把它们两种看成一种来对待,即都是看成循环,分成1循环和不是1循环

看起来就像链表循环一样,我们可以利用快慢双指针来实现

即定义两个指针,fast指针一次走两步,slow指针一次走一步,由于一定会进入循环,那么两个指针由于速度差一定会相遇,我们只需要判断相遇点是否为1就好了

但是撇开这道题的提示,为什么一定会进入循环呢??

有一个原理叫做雀巢原理:

有题目我们知道:

n的最大值为2^31-1 = 2,147,483,647,我们把每一位换成9,即9999999999,即得到在0 ~ 2^31-1 可能出现的最大快乐数:9 * 9 * 10 = 810

即无论快乐数怎么变,最终的范围都在1 ~ 810,由雀巢原理,那么一定不会死循环

题解:

 class Solution {
     public int squaresSum(int n){
         int sum = 0;
         while(n != 0){
             int t = n % 10;
             sum += t * t;
             n /= 10;
         }
         return sum;
     }
     public boolean isHappy(int n) {
         int slow = squaresSum(n);//先让fast和slow各自走一遍
         int fast = squaresSum(squaresSum(n));
         while(slow != fast){
         slow = squaresSum(slow);
         fast = squaresSum(squaresSum(fast));
         }
         return slow == 1;
     }
 }

4.盛水最多的容器

题目:. - 力扣(LeetCode)

此题目的暴力解法当然容易想到,就是一个一个去枚举,最后得出一个最大的即可,但是实际上并不是所有情况都需要枚举:

就举下面的例子来说:

我们定义两个指针分别在一左一右,当我们定住6,从4往前找的时候,会出现两种情况:

(1)数比4大,如5,那么高度还是4,高度不变,但是长度减小,所以体积一定比6 ~ 4的小

(2)数比4小,那么高度减小,长度也减小,所以体积一定比6 ~ 4的体积小

即定住6,从4往前找时,所有的情况都比6 ~ 4 的体积小,那么中间的我们就不必再一一枚举了,直接记录下最大值V1即可

这时候我们将right--,即来到5,

那情况不也是一样,6~5的体积是6~5范围内最大的,记录为V2,

由此我们发现,我们在左右寻找的时候,只需比较left和right的值,如果左边的数小,那就记录下体积,left++;右边小,记录下体积,right--即可

最后得出一个max

题解:

class Solution {
     public int maxArea(int[] height) {
         int left = 0;
         int right = height.length -1;
         int max = 0;
         while(left < right){
             int v = (right - left) * Math.min(height[right],height[left]);
             max = Math.max(v,max);
             if(height[right] > height[left]){
                 left++;
             }else{
                 right--;
             }
         }
     return max;
     }
 }

优化:

就拿这个例子来说,我们在比较完4后,下一个比较2,但是2比4小,体积6~2的体积肯定没有6~4的大,那么2就不用比较,right--,直到有一个比4大,才开始比较.

优化代码:

 class Solution {
     public int maxArea(int[] height) {
         int left = 0;
         int right = height.length -1;
         int max = 0;
          while(left < right){
             int v = (right - left) * Math.min(height[right],height[left]);
             max = Math.max(v,max);
             if(height[right] > height[left] && left < right){
                 int cur = height[left];
                 while(cur >= height[left]){
                     left++;
                 }
             }else{
                 int cur = height[right];
                 while(height[right] <= cur && left < right){
                     right--;
                 }
             }
         }
     return max;
     }
 }

5.有效三角形的个数

链接:. - 力扣(LeetCode)

此题目暴力解法容易想到,即一个一个枚举,但是我们要写3层循环,时间复杂度为O(n^3),再加上三角形的比较:任意两边之和大于第三边,那么还是比较3次,最后时间复杂度来到O(3 * n^3);

我们进行优化:

优化1:三角形的比较:

我们都会想到任意两边之和大于第三边,但是要比较3次

而但我们将3个数先排序后,如2 2 3,那么我们只需要比较2 + 2 > 3 即可,因为2 +2这三个数里面最小的两个数的和,都大于最大的第三边,那么其他两种情况一定成立,那么我们在3层循环中只需要比较一次即可,时间复杂度为O(n^3),加上排序的时间复杂度也小于先前的O(3 * n^3)

优化2:枚举的优化:

排序后:

我们先定住最大的数,利用双指针在最大数前面的范围内依次枚举,查找符合三角形规则的例子,重点就在枚举的过程:

但我们比较left + right 是否大于10时,会出现两种情况:

(1) 大于10:那么2 + 9都大于10了,从left往后找,如3 + 9 4 +9肯定都是大于10的,那么2~5这个范围的数加上9就都满足大于10的条件,因此我们不用一一枚举,就能得到(right - left) 种例子,之后right--即可

(2)小于等于10:

既然2 +5 都小于10了,那么2 加上5以前的数都肯定是小于10的,不必再一一枚举,直接left++即可

当left和right相遇时,说明最大值为10的全部比较完了,接下来让最大值指向9,依次类推……

这样我们发现,会少了很多没必要的比较,时间复杂度会降低很多

题解:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值