【算法】【优选算法】双指针(下)

news/2024/11/5 12:41:54 标签: 算法, 双指针, java

目录

  • 一、611.有效三⻆形的个数
    • 1.1 左右指针解法
    • 1.2 暴力解法
  • 二、LCR 179.查找总价格为目标值的两个商品
    • 2.1 左右指针解法
    • 2.2 暴力解法
  • 三、15.三数之和
    • 3.1 左右指针解法
    • 3.2 暴力解法
  • 四、18.四数之和
    • 4.1 左右指针解法
    • 4.2 暴力解法

一、611.有效三⻆形的个数

题目链接:611.有效三⻆形的个数
题目描述:

题目解析:

  • 返回能够成三角形的三元组合的个数;
  • 三元组合内容相同,但是是原数组中不同的下标的元素,这样的三元组在这道题中是不相同的,要计数。

1.1 左右指针解法

解题思路:

  • 因为我们能构成三角形的证明是两条最小边的和大于最大边,所以我们先将数组排序。
  • 排序后,我们只需要固定一条边,然后使用左右指针在[0, 最大边)区间中去搜索符合条件的边即可。
  • 固定的一条边必须是最大边,因为固定最大边的时候,要让另两边之和变大和变小只有一种走法(即变大左指针向后,变小右指针向前),如果固定最小边,让两边之差变小有两种走法(变小左指针向后和右指针向前都行)。
  • 当左右指针元素和小于最大边,我们只需要将左指针向后走,让元素和更大即可。
  • 当左右指针元素和大于最大边,现在[左指针,右指针) 中的元素和右指针的元素都可以构成三角形了。直接将其中元素个数添加到ret结果中,然后让右指针向前走,让元素和减小即可。

解题代码:

java">//时间复杂度O(n^2)
//空间复杂度O(logn)
import java.util.*;
class Solution {
    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);
        int ret = 0;
        for(int i = nums.length - 1; i > 0; i--) {
            int left = 0;
            int right = i - 1;
            while(left < right) {
                if(nums[right] + nums[left] > nums[i]) {
                    ret += right - left;
                    right--;
                } else {
                    left++;
                }
            }
        }
        return ret;
    }
}

1.2 暴力解法

解题思路:
直接使用三次for循环,将每一种构成三角形的可能都枚举出来,然后一一判断是否符合构成三角形的条件即可。但是这种方法时间复杂度过大,会超时。

解题代码:

java">//时间复杂度O(n^3)
//空间复杂度O(1)
import java.util.*;
class Solution {
    public int triangleNumber(int[] nums) {
        int ret = 0;
        int n = nums.length;
        for(int i = 0; i < n; i++) {
            for(int j = i +1 ; j < n; j++) {
                for(int k = j+1; k < n; k++) {
                    if(nums[i] + nums[j] + nums[k] > 2 * Math.max(nums[i],Math.max(nums[j],nums[k]))) {
                        ret++;
                    }
                }
            }
        }
        return ret;
    }
}

二、LCR 179.查找总价格为目标值的两个商品

题目链接:LCR 179.查找总价格为目标值的两个商品
题目描述:

题目解析:

  • 数组是有序的,只需要找到一组数组中下标不同的两个数的和为target的返回该组合即可。

2.1 左右指针解法

解题思路:

  • 因为数组是有序的,我们可以利用两数的和的单调性来解题。
  • 我们初始左指针指向第一个元素,右指针指向最后一个元素。两个元素的和的单调性具有规律,让和变大让左指针向后移动,让和变小让右指针向前。

解题代码:

java">//时间复杂度O(n)
//空间复杂度O(1)
import java.util.*;
class Solution {
    public int[] twoSum(int[] price, int target) {
       int left = 0, right = price.length-1; 
        while(left < right) {
            if(price[left] + price[right] > target) right--;
            else if(price[left] + price[right] < target) left++;
            else return new int[]{price[left],price[right]};
        }
        return null;
    }
}

2.2 暴力解法

解题思路:

  • 我们直接使用两层for循环将不同的下标的两个元素的和枚举出来比较即可。
  • 但是时间复杂度过大,会超出时间限制。
    解题代码:
java">//时间复杂度O(n^2)
//空间复杂度O(1)
import java.util.*;
class Solution {
    public int[] twoSum(int[] price, int target) {
     for(int i = 0; i < price.length; i++) {
        for(int j = 0; j < price.length; j++) {
            if(price[i] + price[j] == target) {
                return new int[]{price[i],price[j]};
            }
        }
     }
     return null;   
    }
}

三、15.三数之和

题目链接:15.三数之和
题目描述:

题目解析:

  • 数组中三个元素和为0,并且要求其中的元素不能重合,对三元组中先后顺序也没有要求。

3.1 左右指针解法

解题思路:

  • 当我们固定了一个数之后,是不是就是求另两个数的和等于这个固定的数的相反值,这就跟上一道题的思路差不多了,其实也跟第一题三角形思路也相似。
  • 这道题给我们的数组是无序的,这样我们固定一个数之后,我们来求另两个数和时,单调性的控制就不是唯一的,所以我们要将数组排序。
  • 我们使用一个for-i循环先固定一个数,然后使用左右指针在[i+1,nums.length-1] 区间中去三数之和等于0的数,然后将三元组放进结果集,并且左右指针同时移动一步;比0小就左指针向后走让和变大;比0大就右指针向前走。
  • 去重思路:
    • 思路一:
      • 在判断元素和前去重,由于我们数组是有序的,那么相同数值的元素肯定是在一起的,那么我们只要判断移动后的元素与前一步每移动时的值是否相同即可,当相同再次移动到不同即可。
      • 但是这里面有细节问题:当我们使用这样的方法的时候,我们是直接将所有相同的直接跳过,所以我们有可能跳过初始值(也就是i == 0 left == i+1 right == nums.length-1),就如数组是这样[2, 2, 3 ,2],当i = 0 时会跳过直接到第三个元素。所有在while循环条件要加上判断是初始值的条件
      • 还有越界风险:所以在while判断的循环中还要将越界风险排除。
      • 还有我们不知道出判断循环的原因是上面是哪个原因哪一个,所以我们在出来判断循环之后,还要判断是否已经越界或者不符合条件,也就是i >= nums.length 和 left >= right。
    • 思路二:
      • 我们在判断和之后去重,只需要考虑越界风险,和相等即可。但是由于在判断和之中,我们要先将i++ 再去去重,去重后已经到了下一个要进循环的元素了, 所以我们for循环中就不要i++了。
  • 小优化:在这道题中是判断和为0,所以我们当nums[i] > 0,后续[i+1,nums.length-1]空间都是正数,不可能还有结果了,直接出循环。

解题代码

java">//时间复杂度O(n^2)
//空间复杂度O(logn)
写法一:
import java.util.*;
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ret = new ArrayList<>();
        Arrays.sort(nums);
        int len = nums.length;
        for(int i = 0; i < len; i++) {
            if(nums[i] > 0) break;
            //对i去重
            while(i != 0 && i < len && nums[i] == nums[i - 1]) i++;
            if(i >= len) break;
           
            int left = i + 1;
            int right = len - 1;
            while(left < right) {
                while(left < right && left != i+1 && nums[left] == nums[left-1]) left++;//对left去重
                while(left < right && right != len-1 && nums[right] == nums[right+1]) right--;//对right去重
                if(left >= right) break;
               
                if(nums[left] + nums[right] < -nums[i]) left++;
                else if(nums[left] + nums[right] > -nums[i]) right--; 
                else ret.add(Arrays.asList(nums[i],nums[left++],nums[right--]));
            }
        }
        return ret;
    }
}

写法二:
import java.util.*;
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ret = new ArrayList<>();
        Arrays.sort(nums);
        int len = nums.length;
        for(int i = 0; i < len; ) {
            if(nums[i] > 0) break;
            int left = i + 1;
            int right = len - 1;
            while(left < right) {
                if(nums[left] + nums[right] < -nums[i]) left++;
                else if(nums[left] + nums[right] > -nums[i]) right--; 
                else { 
                    ret.add(Arrays.asList(nums[i],nums[left++],nums[right--]));
                    while(left < right && nums[left] == nums[left-1]) left++;//对left去重
                    while(left < right && nums[right] == nums[right+1]) right--;//对right去重
                }
            }
            //对i去重
            i++;
            while(i < len && nums[i] == nums[i - 1]) i++;
        }
        return ret;
    }
}

3.2 暴力解法

解题思路:

  • 我们直接使用三层for循环将每一个三元组枚举出来即可。
  • 去重时我们直接使用HashSet这个集合类即可,所以我们要先将nums排序。
  • 但是会超时。
    解题代码:
java">//时间复杂度O(n^3)
//空间复杂度O(logn)
import java.util.*;
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
         List<List<Integer>> ret = new ArrayList<>();
         Set<List<Integer>> set = new HashSet<>();
         int len = nums.length;
        for(int i = 0; i < len; i++) {
            for(int j = i+1; j < len; j++) {
                for(int k = j+1; k < len; k++) {
                    if(nums[i] + nums[j] + nums[k] == 0) {
                        set.add(Arrays.asList(nums[i],nums[j],nums[k]));
                    }
                }
            }
        }
        ret.addAll(set);

        return ret;
    }
}

四、18.四数之和

题目链接:18.四数之和
题目描述:

题目分析:
跟上面的三数之和,思路一样,代码雷同,只是需要固定两个数即可。

4.1 左右指针解法

解题思路:

  • 思路参考上一道三数之和,两个细节:
  • 这道题有例子是会超过int的最大值的,所以我们需要在计算和的时候使用long来储存。

    解题代码:
java">//时间复杂度O(n^3)
//空间复杂度O(logn)
写法一:
import java.util.*;
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> ret = new ArrayList<>();
        int len = nums.length;
        for(int i = 0; i < len; i++) {
            //对i去重
            while(i < len && i != 0 && nums[i] == nums[i-1]) i++;
            if(i >= len ) break;

            for(int j = i+1; j < len; j++) {
                //对j去重
                while(j < len && j != i+1 && nums[j] == nums[j-1]) j++;
                if(j >= len ) break;

                long newTarget = (long)target - nums[i] - nums[j];
                int left = j + 1;
                int right = len - 1;
                while(left < right) {
                    while(left < right && left != j+1 && nums[left] == nums[left-1]) left++;//对left去重
                    while(left < right && right != len-1 && nums[right] == nums[right+1]) right--;//对right去重
                    if(left >= right) break;

                    long sum = (long)nums[left] + nums[right];
                    if(sum > newTarget) right--;
                    else if(sum < newTarget) left++;
                    else ret.add(Arrays.asList(nums[i],nums[j],nums[left++],nums[right--]));
                }
            }
        }
        return ret;
    }
}
写法二:
import java.util.*;
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> ret = new ArrayList<>();
        int len = nums.length;
        for(int i = 0; i < len;) {
           
            for(int j = i+1; j < len;) {
              

                long newTarget = (long)target - nums[i] - nums[j];
                int left = j + 1;
                int right = len - 1;
                while(left < right) {
                   
                    long sum = (long)nums[left] + nums[right];
                    if(sum > newTarget) right--;
                    else if(sum < newTarget) left++;
                    else {
                        ret.add(Arrays.asList(nums[i],nums[j],nums[left++],nums[right--]));
                        while(left < right && nums[left] == nums[left-1]) left++;//对left去重
                        while(left < right && nums[right] == nums[right+1]) right--;//对right去重
                    }
                }
                //对j去重
                j++;
                while(j < len && nums[j] == nums[j-1]) j++;
            }
            i++;
             //对i去重
            while(i < len && nums[i] == nums[i-1]) i++;

        }
        return ret;
    }
}

4.2 暴力解法

解题思路:

  • 4层for循环,HashSet去重。
  • 会超时。

解题代码:

java">//时间复杂度O(n^4)
//空间复杂度O(logn)
import java.util.*;
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
         Arrays.sort(nums);
         List<List<Integer>> ret = new ArrayList<>();
         Set<List<Integer>> set = new HashSet<>();
         int len = nums.length;
        for(int i = 0; i < len; i++) {
            for(int j = i+1; j < len; j++) {
                for(int k = j+1; k < len; k++) {
                    for(int n = k+1; n < len; n++){
                        if((long)nums[i] + nums[j] + nums[k] + nums[n] == target) {
                            set.add(Arrays.asList(nums[i],nums[j],nums[k],nums[n]));
                        }
                    }
                    
                }
            }
        }
        ret.addAll(set);
        return ret;
    }
}

http://www.niftyadmin.cn/n/5739481.html

相关文章

借助淘宝 API 接口,轻松掌握商品类目信息

借助淘宝 API 接口&#xff0c;轻松掌握商品类目信息 在当今电商蓬勃发展的时代&#xff0c;淘宝作为行业巨头&#xff0c;其海量的商品数据蕴含着无限商机。对于商家、开发者和数据分析者而言&#xff0c;能够准确、快速地获取淘宝商品类目信息是至关重要的。而借助淘宝 API …

【专题】基于服务的体系结构

对于面向服务的体系结构&#xff08;Service-Oriented Architecture&#xff0c;SOA&#xff09;的几种定义&#xff1a; W3C&#xff1a;SOA是一种应用程序体系结构&#xff0c;在这种体系结构中&#xff0c;所有功能都定义为独立的服务&#xff0c;这些服务带有定义明确的可…

Python并发编程——threading

目录 1. 引言1.1 进程与线程的区别1.2 threading 模块的作用与优势 2. threading 的基本使用方法2.1 创建和启动线程2.2 守护线程与非守护线程 3. 线程锁与线程安全3.1 使用 Lock 对象3.2 使用 RLock 对象 4. 线程池的使用4.1 使用线程池处理并发任务4.2 线程池任务的回调函数 …

【rust】rust基础代码案例

文章目录 代码篇HelloWorld斐波那契数列计算表达式&#xff08;加减乘除&#xff09;web接口 优化篇target/目录占用一个g&#xff0c;仅仅一个actix的helloWorld demo升级rust版本&#xff0c; 通过rustupcargo换源 代码篇 HelloWorld fn main() {print!("Hello,Wolrd&…

Python中如何计算整商:详解整除运算及其应用场景

目录 一、整除运算的基本概念 1. 语法 2. 工作原理 二、整除运算的详细解析 1. 整数之间的整除 2. 浮点数之间的整除 3. 整数与浮点数之间的整除 三、整除运算的应用场景 1. 数据处理中的取整操作 2. 循环中的步进控制 3. 分页显示数据 4. 时间计算中的取整 四、整…

鸿蒙HarmonyOS开发:给应用添加基础类型通知和进度条类型通知(API 12)

文章目录 一、通知介绍1、通知表现形式2、通知结构3、请求通知授权 二、创建通知1、发布基础类型通知2、发布进度类型通知3、更新通知4、移除通知 三、设置通知通道1、通知通道类型 四、创建通知组五、为通知添加行为意图1、导入模块。2、创建WantAgentInfo信息。4、创建WantAg…

HTTP、WebSocket、gRPC 或 WebRTC:各种协议的区别

在为您的应用程序选择通信协议时&#xff0c;有很多不同的选择。 本文将了解四种流行的解决方案&#xff1a;HTTP、WebSocket、gRPC 和 WebRTC。 我们将通过深入学习其背后原理、最佳用途及其优缺点来探索每个协议。 通信方式在不断改进&#xff1a;变得更快、更方便、更可靠&…

Source Insight 4.0常用操作

解决中文乱码问题 options->preferences_>file->