当前位置: 首页 > article >正文

BM54-三数之和

题目

给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。

数据范围:0≤n≤1000,数组中各个元素值满足 ∣val∣≤100。

空间复杂度:O(n^2),时间复杂度 O(n^2)。

注意:

  1. 三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
  2. 解集中不能包含重复的三元组。

例如,给定的数组 S = {-10 0 10 20 -10 -40},解集为(-10, -10, 20),(-10, 0, 10)。

示例1

输入:[0]

返回值:[]

示例2

输入:[-2,0,1,1,2]

返回值:[[-2,0,2],[-2,1,1]]

示例3

输入:[-10,0,10,20,-10,-40]

返回值:[[-10,-10,20],[-10,0,10]]


思路1:双指针

实现版本1:

直接找三个数字之和为某个数,太麻烦了,我们是不是可以拆分一下:如果找到了某个数a,要找到与之对应的另外两个数,三数之和为0,那岂不是只要找到另外两个数之和为−a?这就方便很多了。

因为三元组内部必须是有序的,因此可以优先对原数组排序,这样每次取到一个最小的数为a,只需要在后续数组中找到两个之和为−a就可以了,我们可以用双指针缩小区间,因为太后面的数字太大了,就不可能为−a,可以舍弃。

具体做法:

  • step 1:排除边界特殊情况。
  • step 2:既然三元组内部要求非降序排列,那我们先得把这个无序的数组搞有序了,使用sort函数优先对其排序。
  • step 3:得到有序数组后,遍历该数组,对于每个遍历到的元素假设它是三元组中最小的一个,那么另外两个一定在后面。
  • step 4:需要三个数相加为0,则另外两个数相加应该为上述第一个数的相反数,我们可以利用双指针在剩余的子数组中找有没有这样的数对。双指针指向剩余子数组的首尾,如果二者相加为目标值,那么可以记录,而且二者中间的数字相加可能还会有。
  • step 5:如果二者相加大于目标值,说明右指针太大了,那就将其左移缩小,相反如果二者相加小于目标值,说明左指针太小了,将其右移扩大,直到两指针相遇,剩余子数组找完了。

注:对于三个数字都要判断是否相邻有重复的情况,要去重。


代码1.1

import java.util.ArrayList;
import java.util.Arrays;

public class Solution {
    public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
        ArrayList<ArrayList<Integer> > res = new ArrayList<ArrayList<Integer>>();
        int n = num.length;

        //不够三元组
        if(n < 3) {
            return res;
        }

        //排序
        Arrays.sort(num);

        for(int i = 0; i < n - 2; i++) {
            if(i != 0 && num[i] == num[i - 1]) {
                continue;
            }
            //后续的收尾双指针
            int left = i + 1;
            int right = n - 1;
            //设置当前数的负值为目标
            int target = -num[i];

            while(left < right) {
                //双指针指向的二值相加为目标,则可以与num[i]组成0
                if(num[left] + num[right] == target) {
                    ArrayList<Integer> temp = new ArrayList<Integer>();
                    temp.add(num[i]);
                    temp.add(num[left]);
                    temp.add(num[right]);
                    res.add(temp);
                    while(left + 1 < right && num[left] == num[left + 1]) {
                        //去重
                        left++;
                    }
                    while(right - 1 > left && num[right] == num[right - 1]) {
                        //去重
                        right--;
                    }
                    //双指针向中间收缩
                    left++;
                    right--;
                } else if(num[left] + num[right] > target) {  //双指针指向的二值相加大于目标,右指针向左
                    right--;
                } else {  //双指针指向的二值相加小于目标,左指针向右
                    left++;
                }
            }
        }
        return res;
    }
}
  • 时间复杂度:O(n^2),排序的复杂度为O(nlog2n),查找三元组的复杂度为O(n2)。
  • 空间复杂度:O(1),res属于必要空间,不属于额外空间,无其他辅助空间。

实现版本2:

  1. 对数组长度进行特判。
  2. 排序。
  • num[i] > 0说明后面的三数和不可能等于0。
  • 对于重复元素跳过。
  • 左指针left = i + 1,右指针right = len - 1
  • nums[i] + nums[left] + nums[right] == 0执行循环,判断左界和右界是否和下一位置重复,去除重复解。并同时将 left,right 移到下一位置,寻找新的解。
  • 如果和<0,left++;
  • 如果和>0,right--;


代码1.2

import java.util.*;

public class Solution {
    public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
        //存放最终答案的二维数组
        ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
        int len = num.length;

        //特判:长度<3的数组不满足条件
        if (len < 3) {
            return ans;
        }

        //排序O(nlogn)
        Arrays.sort(num);

        for (int i = 0; i < len; i++) {
            //如果nums[i]已经大于0,就没必要继续往后了,因为和就是0啊
            if (num[i] > 0) {
                return ans;
            }
            //注意考虑越界i>0,主要功能是排除重复值
            if (i > 0 && num[i] == num[i - 1]) {
                continue;
            }
            //声明指针
            int cur = num[i];
            int left = i + 1;
            //从尾部开始
            int right = len - 1;
            
            while (left < right) {
                //满足条件的三数和
                int tp_ans = cur + num[left] + num[right];
                //如果已经找到和为0
                if (tp_ans == 0) {
                    //创建一个数组,并将满足条件的三元素放进去
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(cur);
                    list.add(num[left]);
                    list.add(num[right]);
                    //将最终的结果存入答案数组ans中
                    ans.add(list);
                    //判断是left指针指向是否重复
                    while (left < right && num[left] == num[left + 1]) {
                        left++;
                    }
                    //判断是right指针指向是否重复
                    while (left < right && num[right] == num[right - 1]) {
                        right--;
                    }
                    //移动指针
                    left++;
                    right--;
                } else if (tp_ans < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }
        return ans;
    }
}
  • 时间复杂度:O(n^2)。
  • 空间复杂度:O(1)。

思路2:哈希表

利用哈希表特性,对符合条件的三元组进行去重处理。


代码2

import java.util.*;

public class Solution {
    public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
        //存放答案的集合
        ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
        int len =  num.length;

        //排序O(nlogn)
        Arrays.sort(num);

        //哈希表去重
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < len; i++) {
            map.put(num[i], i);
        }

        //若干变量声明
        int L, M, R;
        for (int i = 0; i < len; i = map.get(L) + 1) {
            //指定L的值
            L = num[i];
            //注意里层循环从i+1开始
            for (int j = i + 1; j < len; j = map.get(M) + 1) {
                M = num[j];
                //注意一下,这里是个容易错的细节..
                R = -L - M;
                if (R < M) {
                    break;
                }
                if (map.get(R) != null && map.get(R) > j) {
                    //创建一个数组,并将满足条件的三元素放进去
                    ArrayList<Integer> list = new ArrayList<Integer>();
                    list.add(L);
                    list.add(M);
                    list.add(R);
                    //将最终的结果存入答案数组ans中
                    ans.add(list);
                    //ans.add(Arrays.asList(L,M,R)); 不知道为什么这种写法在牛客平台编译过不了
                }
            }
        }
        return ans;
    }
}


http://www.kler.cn/news/18075.html

相关文章:

  • 盲目自学网络安全只会成为脚本小子?
  • Java入门全网最详细 - 从入门到转行
  • MySQL安装配置教程(保姆级,包含环境变量的配置)适合小白
  • 【Java笔试强训 33】
  • 【python脚本系列】python脚本2——PDF转word文档
  • Rosetta从头蛋白抗体设计、结构优化及在药物研发中的应用
  • Grafana 系列-统一展示-1-开篇
  • 本地使用3台centos7虚拟机搭建K8S集群教程
  • 璞华助力“数字人社”,为成都市人社数字化建设提供多方位的产品与技术支持!
  • Chapter4:频率响应法(下)
  • tiechui_lesson01_入口函数和卸载函数
  • MySQL数据库——MySQL存储函数详解
  • Java版本企业电子招投标采购系统源码之项目说明和开发类型源码
  • [面试题] 判断二维空间中一点是否在旋转矩形内部
  • 活动策划进阶指南:细节决定成败
  • 飞腾ft2000-麒麟V10-SP1安装Docker、运行gitlab容器
  • JSP网络远程作业处理系统(源代码+论文+开题报告+实习报告)
  • 揭秘镭速传输点对点传输技术,NAT+Raysync强强组合
  • 进程替换函数组介绍exec*
  • 嵌入式设备逆向所需的工具链
  • SPSS如何绘制常用统计图之案例实训?
  • 华为MPLS跨域——后门链路实验配置
  • 直线飙升到10万+star的AutoGpt,有多强?帮我写了个网页!
  • 文鼎创智能物联云原生容器化平台实践
  • 为什么网络安全缺口很大,招聘却很少?
  • 乐鑫esp32-c2开发板 烧录演示
  • PMP-项目整合管理
  • MySQL ---- 事务
  • golang - 函数的使用
  • 什么是网络——TCP/IP协议