[230420 leetcode15] 三数之和

题目描述

原题链接

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
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] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

解题思路

1. 初步分析

要找三元组,最原始最暴力的方法就是三层遍历,时间复杂度 O(n^3)。但就像双指针可以把二维遍历降为一维遍历一样,使用双指针也可以把三维遍历降为二维的。

2. 三指针解法

基本思路:

  • 使用三个指针 i、j、k,i 指针指示三元组的第一个数,j 指针和 k 指针相向移动,分别指向三元组的第二个数和第三个数。
  • 对 nums[i]、nums[j]、nums[k] 求和,根据 sum 值大小进行讨论
    • 等于 0:放入结果数组,j、k 同时移动
    • 小于 0:移动 j
    • 大于 0:移动 k

去重逻辑:

  • i 位去重

    //检查下标合法 && 当前i位与前一个i位(nums[i-1])相等
    if(i > 0 && nums[i] == nums[i - 1]){
        continue;
    }
    
  • j 位去重

    //检查下标合法 && 当前j位与前一个j位(nums[j-1])相等
    if(j > i + 1 && nums[j] == nums[j - 1]){
        ++j;
        continue;
    }
    
  • k 位去重(k 是从右向左移动的)

    //检查下标合法 && 当前k位与前一个k位(nums[k+1])相等
    if(k < nums.size() - 1 && nums[k] == nums[k + 1]){
        --k;
        continue;
    }
    

**剪枝逻辑:**核心算法前对数组进行排序,若三元组第三位(即 k 位)元素大于 0,则可跳出当前双指针循环。

3. 代码主干
vector<vector<int>> threeSum(vector<int>& nums) {
    vector<vector<int>> result;
    sort(nums.begin(), nums.end());
    //遍历i位
    for(int i = 0; i < nums.size() - 2; ++i){
        //对i为进行去重
        
        //定义j、k指针
        int j = i + 1, k = nums.size() - 1;
        while(j < k){   //利用双指针降维
            //剪枝
            
            //对j位进行去重
            
            //对k位进行去重
            
            //计算sum值,并对sum值进行讨论
        }
    }
    return result;
}

完整代码

vector<vector<int>> threeSum(vector<int>& nums) {
    vector<vector<int>> result;
    sort(nums.begin(), nums.end());
    for(int i = 0; i < nums.size() - 2; ++i){
        //对i为进行去重
        if(i > 0 && nums[i] == nums[i - 1]){
            continue;
        }
        int j = i + 1, k = nums.size() - 1;
        while(j < k){   //利用双指针降维
            //剪枝
            if(nums[k] < 0) 
                break;
            //对j位进行去重
            if(j > i + 1 && nums[j] == nums[j - 1]){
                ++j;
                continue;
            }
            //对k位进行去重
            if(k < nums.size() - 1 && nums[k] == nums[k + 1]){
                --k;
                continue;
            }
			//计算sum值,并对sum值进行讨论
            int sum = nums[i] + nums[j] + nums[k];
            if(sum == 0){
                vector<int> tmp = {nums[i], nums[j], nums[k]};
                result.push_back(tmp);
                ++j, --k;
            }else if(sum < 0){
                ++j;
            }else if(sum > 0){
                --k;
            }
        }
    }
    return result;
}

关于 unordered_set 的补充

在尝试优化时浅浅使用了一下 unordered_set<vector<int>>,编译报错:

Line 6: Char 36: error: call to implicitly-deleted default constructor of 'unordered_set<vector<int>>'
        unordered_set<vector<int>> result;

搜索了一下 unordered_set 的使用方法,发现 unordered_set 容器的类模板定义如下:

template < class Key,            		//容器中存储元素的类型
           class Hash = hash<Key>,    	//确定元素存储位置所用的哈希函数
           class Pred = equal_to<Key>,	//判断各个元素是否相等所用的函数
           class Alloc = allocator<Key>	//指定分配器对象的类型
           > class unordered_set;

逐个分析:

  1. **容器中存储元素的类型:**在 unordered_set 中,key 和 value 的类型都是一致的,指的就是 key 的类型
  2. **确定元素存储位置所用的哈希函数:**指定元素存储位置的哈希函数,注意:默认哈希函数 hash<Key> 只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。比如说 vector<int> 不属于基本数据类型。
  3. **判断各个元素是否相等所用的函数:**unordered_set 容器内部不能存储相等的元素,而衡量 2 个元素是否相等的标准,取决于该参数指定的函数。 默认情况下,使用 STL 标准库中提供的 equal_to<key> 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。
  4. 指定分配器对象的类型:略
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值