【刷题】双指针进阶

简介: 请看入门篇 :双指针入门

Leetcode 611 有效三角形的个数

来看看给出的测试用例

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

根据题目和样例,这是一个看起来十分简单的题目,让小学生来解决也有可能,毕竟大家都会使用最直接了当的暴力解法,遍历整个数组,筛选出满足的三角形即可。下面我们使用伪代码来简单写一下思路:

for(int i = 0;i < n - 2; i++){
  for(int j = i + 1 ; j < n - 1; j++){
    for(int k = j + 1 ;k < n;k++){
      check(num[i],num[j],num[k]);
    }
  }
}

很显然,这样的算法时间复杂度是O( n^ 3),非常非常非常不理想。

那如何把双指针使用到里面呢?

首先我们来看check的过程:

思路与算法

对于正整数 a,b,c它们可以作为三角形的三条边,当且仅当:

a+b>c
a+c>b
b+c>a

均成立。如果我们将三条边进行升序排序,使它们满足 a≤b≤c,那么 a+c>b 和b+c>a 使一定成立的,我们只需要保证 a+b>c。所以我们只需要比较较小的两个数和最大是否满足即可。

所以现在我们有了一个初步的想法:

  1. 先对容器元素进行排序
  2. 从最大值开始依次作为最大值 num1 来找三角形
  3. 这时开始使用双指针的思想,分别取0(left)下标位置的 num2 和 num1 左边第一个(right)元素 num3,进行比较
  4. 使用单调性判断:
  1. 如果 num2 + num3 > num1 三角形成立,则left 到 right之间的元素都满足 (根据单调性判 断)
  2. 如果 num2 + num3 <= num1那么三角形不成立,那么就要右移 left 来使 num2 + num3更大一些,
  1. 依次往复,就解决问题了。

来看代码:

//比较函数
int compare(int i,int j){
    return i < j;
} 
class Solution {
public:

    int triangleNumber(vector<int>& nums) {
        //首先排序,优化容器
        //取一个固定的较大数
        //在左区间进行查找,利用单调性
        sort(nums.begin(),nums.end(),compare);
        int count = 0;  
        int  m = nums.size() - 1;
        while(m >= 2){
            int max = nums[m];
            int left = 0,right = m - 1;

            while(left <= right){
              //左值是0直接跳过
              //右值是零直接跳出循环(有0不可能成立)
                if(nums[right] == 0) break;
                while (nums[left] == 0) left++; 
                int sum = nums[left] + nums[right];
                if(sum > max) {
                    count += right - left;
                    right--;
                } else left++;
            }
            //每次m 需要进行移动。
            m--;
        }
        return count;
    }
};

这样我们就成功解决了问题。来看运行的效果:

击败 93.48%,我们非常快速!!!(如果使用暴力算法,就直接超时了)

所以双指针的算法是非常高效的算法,并且一般有序问题都可以进行求解!

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

这道题是十分经典的题目,来自剑指offer 的和为 s 的两个数,让我们来看看吧

根据题目描述,也是比较简单的问题,我们一样来进行单调性分析:

  1. 首先也是进行排序
  2. 使用两个指向 left num1 和 right num2 分别从左右开始
  3. 来分析结果:
  1. num1 + num2 == target 直接返回即可
  2. num1 + num2 > target 说明较大,那么right左移即可
  3. num1 + num2 < target 说明较小,那么left右移即可
  1. 直到找到结果就好

来看代码:


class Solution {
public:
   vector<int> twoSum(vector<int>& price, int target) {
      //分别从左右开始遍历
       int left = 0;
       int right = price.size()-1;

       while(left < right){
           int sum = price[left] + price[right];
           //三种结果分析好
           if(sum > target) right--;
           else if(sum < target) left++;
           //返回vector类型 ,使用 { } 包括即可
           else return{price[left] , price[right]};
       }
       //照顾编译器,不然报错
       return {-1,-1};
   }
};

我们这样也成功通过测试用例,完成题目!!!

思路非常直接了当。

Leetcode 15.三数之和

我们先来看给出的样例:

  1. 输入:nums = [-1,0,1,2,-1,-4]
  2. 输出:[[-1,-1,2],[-1,0,1]]
  3. 解释:

     nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。

     nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。

     nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。

     不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。

     注意,输出的顺序和三元组的顺序并不重要。

根据我们的最直接的想法就是遍历,但是我们都知道这样十分复杂,哈哈哈哈

我们来看这道题和判断三角形是不是很像,三角形是要求num1 + num2 > num3,

而这题要求我们num1 + num2 == num3,所以基础的双指针框架是十分相似的,我们可以先写出一个框架来:

class Solution {
public:
   vector<vector<int>> threeSum(vector<int>& nums) {
       //先使数据有序
       sort(nums.begin(),nums.end());
       vector<vector<int>> ans;
    //开始遍历,取最大数
       int m = nums.size() - 1;
       //从 最大值 开始
       while(m >= 2){
    
           int left = 0 , right = m - 1;
           int num1 = nums[m];
           //双指针进行
           while(left < right){
        //左值右值进行初始化
               int num2 = nums[left] , num3 = nums[right];
               //进行判断
               
               if(num2 + num3 + num1 == 0){
               //满足就进行插入
                   ans.push_back({num2,num3,num1}) ;
                       left++;
                       right--;
               } 
               else if(num2 + num3 + num1 < 0) {
                       left++;
               }
               else if(num2 + num3 + num1 > 0) {
                       right--;
               }
           }
               //m 向左移动
               m--;
       }
       return ans;
   }
};

这样提交后:我们只看这一个样例:

发现有好多重复的,这应该怎麽办??????

我的想法是从根源出发,既然我们排好序了就直接每次选中一组后,把相应元素都跳过!!!

不满足的也一次性全部都跳过!!!直接了当

这样一定一定不会有重复的出现!!!

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
       //先使数据有序
        sort(nums.begin(),nums.end());
        vector<vector<int>> ans;
    //从最大值开始遍历
        int m = nums.size() - 1;
        while(m >= 2){
      //左右值进行初始化
            int left = 0 , right = m - 1;
            int num1 = nums[m];
            while(left < right){

                int num2 = nums[left] , num3 = nums[right];
                
                if(num2 + num3 + num1 == 0){
                    ans.push_back({num2,num3,num1}) ;
                    //满足条件直接全部都跳过!!!
                    while(nums[left] == num2 && left < right){
                        left++;
                    }
                    while(nums[right] == num3 && left < right){
                        right--;
                    }
                    
                } 

                else if(num2 + num3 + num1 < 0) {
                //不满足条件的都跳过!!!
                    while(nums[left] == num2 && left < right){
                        left++;
                    }

                }
                else if(num2 + num3 + num1 > 0) {
                 //不满足条件的都跳过!!!
                    while(nums[right] == num3 && left < right){
                        right--;
                    }
                }
            }
            //满足的 m 也都跳过!!!
            while(nums[m] == num1 && m > 0){
                m--;
            }

        }
        return ans;
    }
};

这样我们完成里去除重复的元素!!!!!!

把最复杂的问题简化到了每个步骤里面:

来看效果:

还不错!!!

送给大家一句话:

如今我努力奔跑,不过是为了追上那个曾经被寄予厚望的自己 —— 约翰。利文斯顿

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

相关文章
|
6月前
|
算法 容器
OJ刷题日记:2、双指针(2)
OJ刷题日记:2、双指针(2)
43 0
|
6月前
|
算法
LeetCode刷题---21.合并两个有序链表(双指针)
LeetCode刷题---21.合并两个有序链表(双指针)
|
6月前
|
算法
LeetCode刷题---19. 删除链表的倒数第 N 个结点(双指针-快慢指针)
LeetCode刷题---19. 删除链表的倒数第 N 个结点(双指针-快慢指针)
|
6月前
|
存储 算法
LeetCode刷题---209. 长度最小的子数组(双指针-滑动窗口)
LeetCode刷题---209. 长度最小的子数组(双指针-滑动窗口)
|
6月前
|
存储 容器
LeetCode刷题---11. 盛最多水的容器(双指针-对撞指针)
LeetCode刷题---11. 盛最多水的容器(双指针-对撞指针)
|
6月前
|
算法
LeetCode刷题---167. 两数之和 II - 输入有序数组(双指针-对撞指针)
LeetCode刷题---167. 两数之和 II - 输入有序数组(双指针-对撞指针)
|
3月前
|
Python
【Leetcode刷题Python】138. 复制带随机指针的链表
LeetCode上题目“138. 复制带随机指针的链表”的Python解决方案,包括两种方法:一种是在每个节点后复制一个新节点然后再分离出来形成新链表;另一种是构建一个字典来跟踪原始节点与其副本之间的映射关系,从而处理新链表的构建。
20 1
|
6月前
|
算法 测试技术 容器
【刷题】双指针入门
经过这四道题目的洗礼,我大概对双指针有了初步印象,接下来我会继续努力!!!
76 13
【刷题】双指针入门
|
4月前
|
算法 Java C语言
刷题训练之双指针问题
刷题训练之双指针问题
30 0
|
6月前
|
算法
"刷题记录:哈希表+双指针 | leetcode-2465. 不同的平均值数目 "
该文段是一篇关于编程题目的解答,主要讨论如何找到数组中所有不同平均值的个数。作者首先使用排序和哈希集来解决,将数组转为列表排序后,通过双指针计算平均值并存入哈希集以去重。然后,作者发现可以优化方案,通过双指针在排序后的数组中直接计算两数之和,用哈希集记录不重复的和,从而避免实际计算平均值,提高了算法效率。最终代码展示了这两种方法。
55 0