首页 > 其他分享 >15_三数之和

15_三数之和

时间:2024-09-12 10:25:16浏览次数:10  
标签:right 15 nums 三数 元素 三元组 指针 left

15_三数之和

【问题描述】

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != 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] 。
注意,输出的顺序和三元组的顺序并不重要。

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

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

提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105

【算法设计思想】

1. 数组排序

​ • 对输入数组 nums 进行从小到大的排序。排序后,便于使用双指针技术来查找符合条件的三元组。

2. 遍历数组

​ • 从数组的第一个元素开始,依次遍历每个元素 nums[i]。对于每个元素,目标是找到两个其他元素,使得这三个数的和为零。

3. 跳过重复元素

​ • 如果当前元素 nums[i] 与前一个元素相同(即 nums[i] == nums[i - 1]),则跳过该元素,以避免产生重复的三元组。

4. 初始化双指针

​ • 设置两个指针:left 指向 i 后面的第一个元素(i + 1),right 指向数组的末尾(n - 1)。

5. 双指针查找

​ • 计算当前三元组的和 sum = nums[i] + nums[left] + nums[right]。

​ • 如果 sum == 0,说明找到了符合条件的三元组,将其添加到结果集 ans 中。

​ • 然后移动 left 和 right 指针,跳过重复的元素,以避免重复的三元组。

​ • 如果 sum < 0,说明当前和太小,增大 left 指针的值,即 left++。

​ • 如果 sum > 0,说明当前和太大,减小 right 指针的值,即 right--。

6. 继续查找

​ • 重复第 5 步,直到 left 和 right 指针相遇,结束对当前元素 nums[i] 的查找。

7. 返回结果

​ • 遍历完所有元素后,返回保存所有符合条件三元组的结果集 ans。

【算法描述】

// 寻找数组中所有不重复的三元组,这些三元组的和为零
vector<vector<int>> threeSum(vector<int> &nums)
{
    vector<vector<int>> ans;
    // 对数组进行排序,以便后续操作
    sort(nums.begin(), nums.end());

    // 遍历数组,尝试为每个元素找到所有可能的两数组合,使其和为零
    for (int i = 0; i < nums.size(); i++)
    {
        // 跳过重复的元素,避免重复的三元组
        if (i > 0 && nums[i] == nums[i - 1])
            continue;

        // 初始化左右指针
        int left = i + 1;
        int right = nums.size() - 1;
        // 设置目标值,即当前元素的相反数,用于后续的两数之和的判断
        int target = 0 - nums[i];

        // 当左指针小于右指针时,执行循环
        while (left < right)
        {
            // 判断当前左右指针所指的数之和是否等于目标值
            if (target == nums[left] + nums[right])
            {
                // 找到符合条件的三元组,将其添加到结果中
                ans.push_back({nums[i], nums[left], nums[right]});

                // 移动左指针,跳过重复的元素
                while (left < right && nums[left] == nums[left + 1])
                    left++;
                // 移动右指针,跳过重复的元素
                while (left < right && nums[right] == nums[right - 1])
                    right--;
                // 同时移动左右指针,继续寻找下一组符合条件的数
                left++;
                right--;
            }
            else if (target > nums[left] + nums[right])
            {
                // 如果和小于目标值,移动左指针,增大和的值
                left++;
            }
            else
            {
                // 如果和大于目标值,移动右指针,减小和的值
                right--;
            }
        }
    }

    // 返回所有找到的三元组
    return ans;
}

Python:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 获取列表中元素的数量
        n = len(nums)
        # 对列表进行排序,以便更容易避免重复元素并使用双指针技巧
        nums.sort()
        # 初始化一个空列表来存储结果
        ans: List[List[int]] = []
        
        # 遍历列表
        for i in range(n):
            # 跳过重复的元素,以避免重复的三元组
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            # 初始化两个指针
            left = i + 1
            right = n - 1
            
            # 使用双指针技巧找到和为 -nums[i] 的一对元素
            while left < right:
                current_sum = nums[i] + nums[left] + nums[right]
                
                if current_sum == 0:
                    # 如果和为零,将三元组添加到结果列表中
                    ans.append([nums[i], nums[left], nums[right]])
                    
                    # 跳过左指针的重复元素
                    while left < right and nums[left] == nums[left + 1]:
                        left += 1
                        
                    # 跳过右指针的重复元素
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1
                        
                    # 移动两个指针
                    left += 1
                    right -= 1

                elif current_sum < 0:
                    # 如果和小于零,将左指针向右移动以增加和
                    left += 1
                else:
                    # 如果和大于零,将右指针向左移动以减少和
                    right -= 1

        # 返回三元组列表
        return ans

Java:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        List<List<Integer>> ans = new ArrayList<>();
        // 排序数组,以便使用双指针技术
        Arrays.sort(nums);
        
        for (int i = 0; i < n; i++) {
            // 跳过重复元素
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

            int left = i + 1;
            int right = n - 1;

            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];

                if (sum == 0) {
                    // 找到一个三元组,添加到结果列表
                    ans.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    // 跳过重复元素
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }

                    left++;
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }
        return ans;
    }
}

标签:right,15,nums,三数,元素,三元组,指针,left
From: https://www.cnblogs.com/zeta186012/p/18409678

相关文章

  • C++竞赛初阶L1-15-第六单元-多维数组(34~35课)556: T456506 矩阵转置
    题目内容输入一个 n 行 m 列的矩阵 A,输出它的转置 AT。输入格式第一行包含两个整数 n 和 m,表示矩阵 A 的行数和列数。1≤n≤100,1≤m≤100。接下来 n 行,每行 m 个整数,表示矩阵 A 的元素。相邻两个整数之间用单个空格隔开,每个元素均在 1∼1000 之间。输......
  • C++竞赛初阶L1-15-第六单元-多维数组(34~35课)557: T456507 图像旋转
    题目内容输入一个 n 行 m 列的黑白图像,将它顺时针旋转 90 度后输出。输入格式第一行包含两个整数 n 和 m,表示图像包含像素点的行数和列数。1≤n≤100,1≤m≤100。接下来 n 行,每行 m 个整数,表示图像的每个像素点灰度。相邻两个整数之间用单个空格隔开,每个元素......
  • C++竞赛初阶L1-15-第六单元-多维数组(34~35课)555: T456505 矩阵乘法
    题目内容计算两个矩阵的乘法。n×m 阶的矩阵 A 乘以 m×k 阶的矩阵 B 得到的矩阵 C 是 n×k 阶的,且 C[i][j]=A[i][0]×B[0][j]+A[i][1]×B[1][j]+ …… +A[i][m−1]×B[m−1][j](C[i][j] 表示 C 矩阵中第 i 行第 j 列元素)。输入格式第一行为 n,m,k,表......
  • C++竞赛初阶L1-15-第六单元-多维数组(34~35课)554: T456504 矩阵加法
    题目内容输入两个 n 行 m 列的矩阵 A 和 B,输出它们的和 A+B,矩阵加法的规则是两个矩阵中对应位置的值进行加和,具体参照样例。输入格式第一行包含两个整数 n 和 m,表示矩阵的行数和列数 (1≤n≤100,1≤m≤100)。接下来 n 行,每行 m 个整数,表示矩阵 A 的元素......
  • 【力扣15】三数之和
    15.三数之和-力扣(LeetCode)双指针算法核心:有序(有序了才能使用双指针)因此,先排序,且保证i<j<k的顺序;顺序确定,双指针才能有序移动,可以将原本o(n2)复杂度降未o(n)双指针:先想暴力做法,再看有没有单调性,有单调性就用双指针有三个数,但只有双指针,所以先枚举一个数;后两个数按照双指针,根......
  • P3515
    高效高效分块。here#include<bits/stdc++.h>usingnamespacestd;intn,a[500010];doubledp[500010],sqr[500010];doublew(intj,inti){ returndouble(a[j])+sqr[i-j];}voidwork(intl,intr,intL,intR){ if(l>r)return; intmid=l+r>>1,p; d......
  • P10315 解题报告
    题目传送门题目大意:有\(n\)个石碑,每个石碑有\(0\simm-1\)共\(m\)种状态,击打一个石碑会带动其他的石碑。若当前石碑的状态是\(s\),则击打或被带动后的状态为\((s+1)\bmodm\)。现给定这\(n\)个石碑的初始状态\(s_i\)、每个石碑带动的石碑及末状态\(t_i\),求每个......
  • 南沙C++信奥老师解一本通题: 1315:【例4.5】集合的划分
    ​ 【题目描述】【输入】给出n和k。【输出】n个元素a1,a2,……,an放入k个无标号盒子中去的划分数S(n,k)。【输入样例】106 【输出样例】22827 #include<iostream>usingnamespacestd;longlongSplit(intn,intplate)//等同于n个不同的数......
  • 用于营销的15种电子邮件类型
    你可能听过这些建议——发送个性化、有针对性的邮件,欢迎订阅者加入你的列表,识别客户的重要时刻和庆祝活动,等等。但你是否知道有不同类型的邮件可以帮助你实现这些目标呢?你不一定要全部使用,但这份列表可以帮助你找到与受众沟通的最佳方式。以下是15种适用于各种目的的营销邮件......
  • 五星级可视化页面(15):各类医疗场景下大屏页面
    可视化大屏在医疗领域有许多重要的价值和应用:1.数据监控和实时展示:可视化大屏可以用于监控医疗设备、患者数据、手术过程等,实时展示医疗数据的变化和趋势,帮助医护人员及时发现异常情况并做出相应的处理。2.医院运营管理:可视化大屏可以展示医院的运营数据,包括门诊量、......