java数据结构与算法刷题-----LeetCode15. 三数之和

news2024/11/15 5:59:09
java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846

在这里插入图片描述

解题思路
  1. 和LeetCode1.两数之和一样,但是这道题边界条件更多。
  2. 两数之和那道题目中,我们使用了map进行处理,也讲了如果投机取巧,用大量的数组空间充当map集合,从而达成相同的逻辑处理代码,只是将map换成数组,却比map集合快很多的办法。
  3. 这道题也同样可以用map集合,但是因为边界条件过多,代码十分繁琐,尤其是投机取巧用数组充当hash,确实在做题方面,可以达成超越100%的用户,但是没有任何实际意义,只能做题。(后面也会将代码给出)
  4. 所以这道题,我会给出比较靠谱的办法,时间复杂度也不高,工作中推荐使用。那就是排序+双指针
  5. 先对整个数组排序,然后创建3个指针,从第一个数开始向后枚举
  6. 第一个指针 i 只是起到限定第一个数的作用,我们需要通过双指针left和right,来找到第一个数的右边区域的另外两个数
  7. 初始left左指针指向第一个数 i 的右边第一个位置,而right指向右边区域末尾,如果3个数相加比0大,就说明right指向的数太大,right–即可,反之,如果相加比0小,left就太小了,进行left++。
代码,时间复杂度O(n^2),空间复杂度,数组需要排序,工作场景中,不可以改变原数组,因此需要O(n)空间复杂度来排序。然后排序算法使用快速排序,需要O(logN)的栈空间复杂度。
  1. 方法一:排序+双指针,效率肯定比不过方法二的hash表,而且使用数组当hash表,但是实际工作中,肯定使用方法一。此方法耗时25ms, 方法二耗时0ms
    在这里插入图片描述
class Solution {

    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);//先排序,为了双指针枚举提供方便
        List<List<Integer>> ans = new ArrayList<>();//答案需要的链表
        int n = nums.length;//n代表数组长度
        for (int i = 0; i < n - 2; ++i) {//因为需要3个数,所以第一个数最多到---倒数第3个
            int x = nums[i];//x保存当前遍历的第一个数
            if (i > 0 && x == nums[i - 1]) continue; // 跳过重复数字,枚举过的,就不重复枚举了
            // 优化一:因为数组排序后是从小到大,如果当前第一个数+它后面两个数,就已经>0了,那当前枚举,包括后面的,都不会符合条件
            if (x + nums[i + 1] + nums[i + 2] > 0) break; 
            // 优化二:如果当前数+倒数那两个数(从小到大排序,也就是末尾的都是最大的)都小于0的话,那也就不用在考虑这个枚举了。肯定枚举不出来
            if (x + nums[n - 2] + nums[n - 1] < 0) continue; 
            //左右指针
            int left = i + 1, right = n - 1;
            while (left < right) {//只要还有元素可以枚举就继续
                int s = x + nums[left] + nums[right];//3个数的和
                if (s > 0) --right;//如果s比0大,说明right指向的太大的,right--
                else if (s < 0) ++left;//如果s比0小,说明left指向太小,left++
                else {//如果s = 0,说明找到了,添加到链表,然后将继续进行下次枚举
                    ans.add(List.of(x, nums[left], nums[right]));//添加到链表
                    //进行下次枚举之前,我们可以跳过已经枚举过的,也就是和当前left和right数字重复的
                    //因为答案中不要重复的元素
                    for (++left; left < right && nums[left] == nums[left - 1]; ++left); // 跳过重复数字
                    for (--right; right > left && nums[right] == nums[right + 1]; --right); // 跳过重复数字
                }
            }
        }
        return ans;//返回答案
    }
}
  1. 方法二:使用hash表,用数组充当hash,空间复杂度不确定,取决于数组中存储的值的范围,如果一共有两个元素,[1,999999],那么空间复杂度就是999999. 所以,这是投机取巧的方法,但是做题的效率确实快。只用了0ms
    在这里插入图片描述
//C(n, k) = C(n - 1, k) + C(n - 1, k - 1)
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        return nSum(nums, 3, 0);
    }

    public List<List<Integer>> fourSum(int[] nums, int target) {
        return nSum(nums, 4, target);
    }

    public List<List<Integer>> nSum(int[] nums, int k, int target) {
        return new AbstractList<List<Integer>>() {
            final List<List<Integer>> res = new ArrayList<>();
            final List<Integer> path = new ArrayList<>();
            long min;

            @Override
            public List<Integer> get(int index) {
                init();
                return res.get(index);
            }

            @Override
            public int size() {
                init();
                return res.size();
            }

            public void init() {
                if (res.isEmpty()) {
                    int n = nums.length;
                    long[] Arr = new long[n];
                    Arrays.sort(nums);
                    min = nums[0];
                    for (int i = 0; i < n; i++) {
                        Arr[i] = nums[i] - min;
                    }
                    long NewTarget = (long)target - (long)k * min;
                    C(false, Arr, n, k, NewTarget);
                }
            }

            //C(n, k) = C(n - 1, k) + C(n - 1, k - 1)
            public void C(boolean T, long[] a, int n, int k, long target) {
                if (n == 0 || k == 0) {
                    if (target == 0 && k == 0) {
                        res.add(new ArrayList<>(path));
                    }
                    return;
                }
                if (k == 2) {
                    if (!T && n != a.length && a[n] == a[n - 1]) {
                        return;
                    }
                    //两数之和模板
                    twoSum(a, 0, n - 1, target);
                    return;
                }
                if (n == k) {
                    if (!T && n != a.length && a[n] == a[n - 1]) {
                        return;
                    }
                    //数组中元素和是否等于target
                    sumArr(a, n, target);
                    return;
                }
                if (check(a, n, k, target)) {
                    return;
                }
                C(false, a, n - 1, k, target);
                if (!T && n != a.length && a[n] == a[n - 1]) {
                    return;
                }
                if (target - a[n - 1] >= 0) {
                    path.add((int) (a[n - 1] + min));
                    C(true, a, n - 1, k - 1, target - a[n - 1]);
                    path.remove(path.size() - 1);
                }
            }

            void twoSum(long[] a, int l, int r, long target) {
                if (l >= r || a[r - 1] + a[r] < target || a[l] + a[l + 1] > target) {
                    return;
                }
                while (r > l) {
                    long sum = a[l] + a[r];
                    if (sum < target) {
                        l++;
                    } else if (sum > target) {
                        r--;
                    } else {
                        path.add((int) (a[l] + min));
                        path.add((int) (a[r] + min));
                        res.add(new ArrayList<>(path));
                        path.remove(path.size() - 1);
                        path.remove(path.size() - 1);
                        while (r > l && a[l] == a[l + 1]) {
                            l++;
                        }
                        while (r > l && a[r] == a[r - 1]) {
                            r--;
                        }
                        l++;
                        r--;
                    }
                }
            }

            void sumArr(long[] a, int n, long target) {
                for (int i = n - 1; i > -1; i--) {
                    target -= a[i];
                    path.add((int) (a[i] + min));
                }
                if (target == 0) {
                    res.add(new ArrayList<>(path));
                }
                for (int i = n - 1; i > -1; i--) {
                    target += a[i];
                    path.remove(path.size() - 1);
                }
            }

            boolean check(long[] a, int n, int k, long target) {
                if (n - k < 0) {
                    return true;
                }
                long max = 0;
                long min = 0;
                for (int i = 0; i < k; i++) {
                    min += a[i];
                    max += a[n - i - 1];
                }
                if (target < min || target > max) {
                    return true;
                }
                return false;
            }
        };
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1429767.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter 各种Demo效果合集

Flutter 各种Demo实现效果&#xff1a; github&#xff1a;GitHub - PangHaHa12138/FlutterDemo: Flutter 各种Demo效果合集 1&#xff1a;2种 仿朋友圈 效果,顶部拉伸 和 不拉伸 2&#xff1a;仿抖音上下滑动视频播放 3&#xff1a;视频直播&#xff08;使用的电视台的m3u…

云计算基础(云计算概述)

目录 一、云计算概述 1.1 云计算的概念 1.1.1 云计算解决的问题 1.1.2 云计算的概念 1.1.3 云计算的组成 1.2 云计算主要特征 1.2.1 按需自助服务 1.2.2 泛在接入 1.2.3 资源池化 1.2.4 快速伸缩性 1.2.5 服务可度量 1.3 云计算服务模式 1.3.1 软件即服务(Softwar…

C#验证字符串是否包含汉字:用正则表达式 vs 用ASCII码 vs 用汉字的 Unicode 编码

目录 一、使用的方法 1.使用正则表达式验证字符串 2.使用正则表达式验证字符 3.用ASCII码判断 4.用汉字的 Unicode 编码范围判断 二、实例 1.源码 2.生成效果 验证一个字符串是否是纯汉字或者包含有汉字的前提&#xff0c;是VS编辑器的默认编码格式设置为&#xff1a;选…

Vite与Webpack打包内存溢出问题优雅处理方式

Vite与Webpack打包内存溢出问题处理 文章目录 Vite与Webpack打包内存溢出问题处理1. Vite1. 打包错误提示2. 命令行方式解决3. 配置环境变量方式解决1. 设置变量2. 配置系统的环境变量 2. Webpack1. 打包错误提示2. 命令行方式解决3. 配置环境变量方式解决1. 设置变量2. 配置系…

Llama2大模型开源,大模型的Android时代来了?

就昨天凌晨,微软和Meta宣布Llama2大模型开源且进一步放开商用,一下朋友圈刷屏。要知道,开源界最强大的模型就是过去Meta开源的Llama,而现在Llama2更强大,又开放商用,更有微软大模型霸主企业撑腰(微软既投资大模型界的IOS——ChatGPT,又联合发布大模型的Android——Llam…

旧衣物回收小程序开发,互联网模式下的营收有多大?

在当下快节奏的生活中&#xff0c;人们不仅生活水平在提高&#xff0c;消费水平也在逐渐提高&#xff0c;从而导致了闲置衣物的增加。为了减少浪费&#xff0c;旧衣服回收行业受到了大众的广泛关注&#xff0c;成为循环利用的一大方式。 当然&#xff0c;在当下网络时代&#…

Docker 阿里云镜像仓库CR使用实践

一、使用容器镜像&#xff0c;查看镜像&#xff0c;创建&#xff0c;推送&#xff0c;拉取阿里云镜像 CR镜像管理&#xff08;阿里云容器镜像服务&#xff08;Container Registry&#xff09;&#xff09; 登录实例 未创建的镜像名称也可以push、docker的私有仓库需要提起创建…

C语言——深入理解指针(1)

目录 1.内存和地址 a 内存的理解 b 如何理解编址 2.指针变量和地址 a 取地址操作符 b 指针变量 c 解引用操作符 d 指针变量的大小 1.内存和地址 a 内存的理解 假想这样一个场景&#xff0c;你的朋友找你玩&#xff0c;到了你家小区&#xff0c;如何让她迅速的找到…

时间复杂度为 O(n) 的排序算法

大家好&#xff0c;我是 方圆。本文介绍线性排序&#xff0c;即时间复杂度为 O(n) 的排序算法&#xff0c;包括桶排序&#xff0c;计数排序和基数排序&#xff0c;它们都不是基于比较的排序算法&#xff0c;大家重点关注一下这些算法的适用场景。 桶排序 桶排序是分治策略的一…

Sentinel应用笔记

概念 当A、B、G、H掉线&#xff0c;其他服务就没法通信了 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。 特性…

如何在PS5上使用金手指修改游戏

环境&#xff1a;windows PS5 问题&#xff1a;PS5 没有GodHen&#xff0c;无法使用json金手指&#xff0c;PKG金手指比较少 解决办法&#xff1a;使用MultiTrainerv从网络注入PS5&#xff0c;修改进程内存 背景&#xff1a;为了护肝&#xff0c;拒绝刷刷刷 解决过程&#xff…

切换登录时,清空input输入框

在uniapp登录页面时&#xff0c;会出现几种登录方式&#xff0c;当切换登录方式时&#xff0c;会出现input复用问题。像下面图中所示。 出现复用的原因是&#xff1a;Vue在进行Dom渲染时&#xff0c;出于性能考虑&#xff0c;会复用已经存在的元素&#xff0c;而不是重新创建新…

计算机网络——链路层(1)

计算机网络——链路层&#xff08;1&#xff09; 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c; [跳转到网站](https://www.captainbed.…

【Spark实践6】特征转换FeatureTransformers实践Scala版--补充算子

本节介绍了用于处理特征的算法&#xff0c;大致可以分为以下几组&#xff1a; 提取&#xff08;Extraction&#xff09;&#xff1a;从“原始”数据中提取特征。转换&#xff08;Transformation&#xff09;&#xff1a;缩放、转换或修改特征。选择&#xff08;Selection&…

freertos 源码分析二 list链表源码

list.c 一、链表初始化 void vListInitialise( List_t * const pxList ) { pxList->pxIndex ( ListItem_t * ) &…

Python flask 模板详解

文章目录 1 概述1.1 模板简介1.2 templates 文件1.3 简单应用 2 模板语法2.1 for 循环2.2 if 判断 3 模板的继承3.1 格式要求3.2 实现示例3.3 复用父模板的内容&#xff1a;super 1 概述 1.1 模板简介 定义&#xff1a;定义好的 html 文件&#xff0c;用于快速开发 web 页面J…

图像处理python基础

array 读取图片 tensor 模型预测 一般过程&#xff1a;读取数据np->tensor->model->result->np->画图 shape确保图像输入输出尺寸正确 读取图片 将在GPU上运行的tensor类型转变成在CPU上运行的np类型 三类计算机视觉任务的输入&#xff1a; 分类&#xff1…

【更新】中国统计摘要2023年(PDF+excel格式)

《中国统计摘要》是一部专为及时展现中国国民经济与社会发展状况所编辑的综合统计著作。2023版的《中国统计摘要》为我们呈现了2022年的重要社会经济指标数据&#xff0c;并同时为读者提供了自1978年以来的历史数据摘要。需要特别注意的是&#xff0c;为了确保这本书的及时性&a…

概率论中的全概率公式、贝叶斯公式解析

全概率公式 定义 全概率公式是用来计算一个事件的概率&#xff0c;这个事件可以通过几个互斥事件的并集来表示。这几个互斥事件称为“完备事件系”。实质是由原因推结果。 公式 用途 全概率公式通常用于计算一个事件的总概率&#xff0c;特别是当这个事件与几个不同的因素相关…

Javascript入门:第三个知识点:javascript里的数据类型、运算符

数字类型 123 //整数 123.1 //浮点数 1.123e3 //科学计数法 -10 //负数 NaN //not a number Infinity //无限大 以上的类型在javascript里都是数字类型 字符串类型 在开始之前&#xff0c;我需要先说明白两个知识点&#xff1a; console.log()是啥&#xff1f; let 与 v…