此专栏文章是对力扣上算法题目各种方法总结和归纳, 整理出最重要的思路和知识重点并以思维导图形式呈现, 当然也会加上我对导图的详解.

目的是为了更方便快捷的记忆和回忆算法重点(不用每次都重复看题解), 毕竟算法不是做了一遍就能完全记住的. 所以本文适合已经知道解题思路和方法, 想进一步加强理解和记忆的朋友, 并不适合第一次接触此题的朋友(可以根据题号先去力扣看看官方题解, 然后再看本文内容).

关于本专栏所有题目的目录链接, 刷算法题目的顺序/注意点/技巧, 以及思维导图源文件问题请点击此链接. 欢迎和博主一起打卡刷力扣算法, 博主同步更新了算法视频讲解 和 其他文章/导图讲解, 更易于理解, 欢迎来看!

目录

0.导图整理

1.对于不重复三元组的处理

2.双指针的思想

3.元素为整型链表的数组链表: ArrayList>()

源码 


 题目链接:  https://leetcode-cn/problems/3sum/

0.导图整理

1.对于不重复三元组的处理

本题最大的难点在于题目要求的 不重复的三元组, 三数之和 不像 两数之和 那样简单, 它的可重复情况是非常多的, 无法像 两数之和 那样, 只要将第一个元素放入哈希表中, 就可以轻松解决元素重复的问题了. 

对于三数之和, 即使使用哈希表去重, 它的操作也是比较困难的. 所以我们不能简单地使用三重循环枚举所有的三元组, 然后使用哈希表进行去重操作, 这样的工作量比较大.

因此我们必须换一种方法来解决此题, 从源头上解决元素重复的问题. 如果给定的数组是有序的, 那么其中可重复的情况就是完全可以控制的了, 处理起来也是很简单的. 所以我们首先将数组中的元素从小到大进行排序, 随后使用普通的三重循环就可以满足上面的要求.

我们会发现其中重复的元组都是相邻的元组, 只需要保证在每一重循环时, 相邻两次枚举的元素不是相同的元素, 这样就可以避免元组重复的情况了. 也就是导图中每层循环时的 if判断语句.

2.双指针的思想

使用普通的三层循环确实也能解决问题, 但是O(n^3)的时间复杂度也确实太高了, 这时我们想到了在 有序数组的两数之和 中使用的双指针的方式(双指针在此文已说明清楚, 这里就不重复讲解了), 虽然现在是三数之和, 但当我们正常遍历了第一层循环之后, 剩下的两个数不就形成了 有序数组的两数之和 了吗? 所以我们只要 保持第二重循环不变, 而将第三重循环变成一个从数组最右端开始向左移动的指针, 同时加上上述讨论的避免重复的条件, 最终代码就完成了.

时间复杂度也从O(n^3)降到了O(n^2), 至于空间复杂度, 有两种情况: 如果允许我们改变原来的数组, 那么只需要排序算法额外的空间复杂度O(logN), 如果不允许的话, 那就要使用了一个额外的数组存储了nums的副本并进行排序,空间复杂度为 O(N).

3.元素为整型链表的数组链表: ArrayList<List<Integer>>()

这里补充说明下这个数据结构, 对于新手来说, 看着还是挺吓人的呢! 我们从外到里依次来拆分这个结构. 首先最外面 ArrayList() 表明它的本质是一个数组链表, 无论里面的元素是什么类型, 它最本质的结构都不会发生变化的. 再来看它里面所装的结构是 List<Integer>, 就是说这个数组链表中的每一个元素都是一个链表List, 长度可以是任意的长度, 但是这个链表List中的元素的类型必须是整型Integer. 这里的<>用到了java中的 泛型 的概念, 简单来说就是一个写一个通用的模板, 里面所包含元素的类型可以是任意的, 具体使用时对其指定具体的类型即可. 对于较复杂的数据类型, 一般都是采用这种由外到内的分析方法.

对应本题来说下具体的应用: ans.add(Arrays.asList(nums[first],nums[second],nums[third])), 首先ans这个数组链表使用.add函数添加了一个链表, 而这个链表是通过 3个整型数据 由Arrays.asList()这个方法转换成的链表, 这样就成功添加了一个链表. 之后每次循环, 只要有满足条件的三元组都会添加一个链表, 最终所有满足条件的三元组链表形成了最终结果的数组链表ans.

对于java和C++这种强类型的语言, 使用时要明确指明所用到的数据类型, 但对于Python这种弱类型的语言来说, 用起来就非常简单了, 你只需要创建了链表list(), 不需要指明任何类型, 使用时直接添加某种特定类型的数据即可. 而且每次添加的数据会自动保存为一个链表的形式添加到链表中, 不需要显式地自己将其转化为链表, 用起来还是很简单的.

源码 

Python: 

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]: # 元素为整型链表的数组链表
        n = len(nums)
        nums.sort()   # 将数组进行排序
        ans = list()
        
        # 枚举 a
        for first in range(n):
            # 需要和上一次枚举的数不相同
            if first > 0 and nums[first] == nums[first - 1]:
                continue
            # c 对应的指针初始指向数组的最右端
            third = n - 1
            target = -nums[first]
            # 枚举 b
            for second in range(first + 1, n):
                # 需要和上一次枚举的数不相同
                if second > first + 1 and nums[second] == nums[second - 1]:
                    continue
                # 需要保证 b 的指针在 c 的指针的左侧
                while second < third and nums[second] + nums[third] > target:
                    third -= 1
                if second == third: # 如果指针重合,后续也不会满足条件,可以退出循环
                    break
                if nums[second] + nums[third] == target:
                    ans.append([nums[first], nums[second], nums[third]])
        
        return ans

java:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums); //将数组进行排序
        List<List<Integer>> ans = new ArrayList<List<Integer>>(); //元素为整型链表的数组链表
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                if (second == third) { // 如果指针重合,后续也不会满足条件,可以退出循环
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.add(Arrays.asList(nums[first],nums[second],nums[third]));
                }
            }
        }
        return ans;
    }
}

感觉作者写的不错的, 别忘了点赞关注加收藏哦(一键三连)!你的支持会带给我极大的动力, 写出更多优秀文章!

我的更多精彩文章链接, 欢迎查看

各种电脑/软件/生活/音乐/动漫/电影技巧汇总(你肯定能够找到你需要的使用技巧)

力扣算法刷题 根据思维导图整理笔记快速记忆算法重点内容(欢迎和博主一起打卡刷题哦)

 

计算机专业知识 思维导图整理

最值得收藏的 Python 全部知识点思维导图整理, 附带常用代码/方法/库/数据结构/常见错误/经典思想(持续更新中)

最值得收藏的 C++ 全部知识点思维导图整理(清华大学郑莉版), 东南大学软件工程初试906科目

最值得收藏的 计算机网络 全部知识点思维导图整理(王道考研), 附带经典5层结构中英对照和框架简介

最值得收藏的 算法分析与设计 全部知识点思维导图整理(北大慕课课程)

最值得收藏的 数据结构 全部知识点思维导图整理(王道考研), 附带经典题型整理

最值得收藏的 人工智能导论 全部知识点思维导图整理(王万良慕课课程)

最值得收藏的 数值分析 全部知识点思维导图整理(东北大学慕课课程)

最值得收藏的 数字图像处理 全部知识点思维导图整理(武汉大学慕课课程)

红黑树 一张导图解决红黑树全部插入和删除问题 包含详细操作原理 情况对比

各种常见排序算法的时间/空间复杂度 是否稳定 算法选取的情况 改进 思维导图整理

人工智能课件  算法分析课件  Python课件  数值分析课件  机器学习课件 图像处理课件

考研相关科目 知识点 思维导图整理

考研经验--东南大学软件学院软件工程(这些基础课和专业课的各种坑和复习技巧你应该知道)

东南大学 软件工程 906 数据结构 C++ 历年真题 思维导图整理

东南大学 软件工程 复试3门科目历年真题 思维导图整理

最值得收藏的 考研高等数学 全部知识点思维导图整理(张宇, 汤家凤), 附做题技巧/易错点/知识点整理

最值得收藏的 考研线性代数 全部知识点思维导图整理(张宇, 汤家凤), 附带惯用思维/做题技巧/易错点整理

高等数学 中值定理 一张思维导图解决中值定理所有题型

考研思修 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理

考研近代史 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理

考研马原 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理

考研数学课程笔记  考研英语课程笔记  考研英语单词词根词缀记忆  考研政治课程笔记

Python相关技术 知识点 思维导图整理

Numpy常见用法全部OneNote笔记     全部笔记思维导图整理

Pandas常见用法全部OneNote笔记     全部笔记思维导图整理

Matplotlib常见用法全部OneNote笔记  全部笔记思维导图整理

PyTorch常见用法全部OneNote笔记    全部笔记思维导图整理

Scikit-Learn常见用法全部OneNote笔记  全部笔记思维导图整理

Java相关技术/ssm框架全部笔记

Spring  springmvc  Mybatis  jsp

科技相关 小米手机

小米 红米 历代手机型号大全 发布时间 发布价格

常见手机品牌的各种系列划分及其特点

历代CPUGPU的性能情况和常见后缀的含义 思维导图整理

更多推荐

❤️导图整理数组4: 三数之和 相比于 两数之和 的难点, 力扣15❤️