LeetCode 刷题(一)

三数组求和问题

Posted by Iven on July 9, 2023

#前言

这个暑假在家里闲着也是闲着,准备在LeetCode上每天打卡一道算法题目,学学新的知识,也方便提升一下自己。 今天做的是第15题。 ##题目详情

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

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

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

##示例

输入: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]

注意,输出的顺序和三元组的顺序并不重要。

##解题思路

1.首先我想到的是直接暴力求解,但是当给定的数组到达一定的规模的时候,效率无疑会低很多,我认为暴力求解永远都是存在于最后一个解题思路的方法,因此首先排除。

2.那么就得从给定数组本身来进行求解,题目给定的数组是无序的,对于相加求和的题目来说,十分不方便,那么便可直接对给定数组进行重新排序,使用sort函数即可。

3.排完序之后,就要按顺序查出给定数组的其中三个元素之和为0的情况。这里使用了双指针的方法。

###双指针

双指针方法,是算法题目中较为常见的一种解决方法。其核心思想是在数组中设置两个指针,分别指向数组的头和尾,这样可以同时判定两个元素是否满足题目所要求的条件。在遍历时,设置的终止条件为头指针小于尾指针,每次遍历,依据条件选择让头指针向后移或者尾指针向前移动。 但是,本题所要求的是给定数组中三个元素的和,那么思路就很清晰了:**使用一个单独的指针`k`指向第一个元素,设置双指针法的两个指针分别为`i`、`j`,分别指向`k`后的“头”和“尾”。**意思就是,三元素之和中的固定元素是指针`k`指向的元素,剩下两个元素使用双指针法来遍历。每次双指针法遍历完后,将指针`k`向后移动,重复上述遍历。

##实现细节

1.首先是使用指针k对数组进行遍历时,要确定一个终止条件为:如果nums[k]>0那么直接终止判断,返回,因为数组已经有序,指针k所指向的元素为三个元素中最小的元素,如果它都大于0,那么比不可能出现三元素之和为0的情况了。

2.最重要的一点是去除重复数组,题目所要求的为输出的三元数组,不能包含元素相同的数组,但是给定数组的时候,里面的很多元素是可以重复的,当我们在进行遍历的时候,就会出现相同的三元组,那么就要去除重复的三元组。 ###去重

1.首先是在大循环(即用指针k遍历给定数组时),如果指针k向后移动指向的元素与原来的元素数值相同,那么直接continue这次循环,因为得到的三元数组一定是和上一轮得到的三元组相同的结果。

2.其次,是在双指针遍历时进行去重,样例为-2,0,0,0,2,2,2时,k指针指向-2i指针指向0j指针指向2。在进行双指针遍历时,i指针向后移动一次或者j指针向前移动一次,都可以组成三元组,但是三元组与上一轮得到的三元组元素完全相同,每次移动i指针或者j指针之后都要加上一个while循环判断:如果nums[i]==nums[i-1],那么再令i++。对于指针j同理。

#代码实现

class Solution { public:

vector<vector<int>> threeSum(vector<int>& nums) {
    int len=nums.size();//给定数组的长度
    vector<vector<int>> a;//要求返回的数组
 		int k=0;//作为标准的指针
	if(len<3){
		return a;
	}
    sort(nums.begin(),nums.end());//对给定数组进行从小到大排序
    for(int k=0;k<len;k++){
        if(nums[k]>0){
            return a;
        }
        int i=k+1;//头指针
        int j=len-1;//尾指针
        if(k>0&&nums[k]==nums[k-1]){//标准指针指向的元素与上一个元素相同则去掉,排除重复
            continue;
        }
        while(i<j){//双指针法,左指针一定要小于右指针
            if(nums[i]+nums[j]+nums[k]==0){
            a.push_back({nums[k],nums[i],nums[j]});//若三者相加为0,则加入此三元组
            }
            if(nums[i]+nums[j]+nums[k]<0){//若三者相加小于0,那么就让头指针后移 
            i++;
            continue;
            }
            if(nums[i]+nums[j]+nums[k]>0){//若三者相加大于0,则让尾指针前移
            j--;
            continue;
            }
            i++;
            while(nums[i]==nums[i-1]&&i<j){
            i++;
            }
            j--;                                        
            while(nums[j]==nums[j+1]&&i<j){
            j--;
        }
    }
    }
    return a; }

};