当前位置: 首页 > news >正文

leetcode15 三数之和

1.哈希法

为了避免重复

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> temple;//使用 set 来存储符合条件的三元组,避免重复
        vector<vector<int>> out;//存放最终输出结果

        sort(nums.begin(),nums.end());//先进行数组排序

        for(int i = 0; i < nums.size(); i++){
            if(i > 0 && nums[i] == nums[i-1]) continue;//如果当前数值与上一轮循环所得数值相同、后续找到的三元组将相同,没必要再找,退出本轮
            unordered_set<int> hash;//定义哈希表存放待查找内容
            for(int j = i+1; j < nums.size(); j++){                
                if(hash.find(-(nums[i] + nums[j])) != hash.end()){
                    temple.insert({nums[i], nums[j], -(nums[i] + nums[j])});//哈希表中存在与当前nums[i]\nums[j]匹配的值 -(nums[i] + nums[j]),即找到了符合条件的三元组将其存入                 
                }
                hash.insert(nums[j]);//将nums[j]存入,可能成为后续寻找的-(nums[i] + nums[j])值
            }    
        }
        for(const auto& t: temple){
            out.push_back(t);
        }//遍历三元组将他们存入最终输出的变量中
        return out;
    }
};

在解决 三数之和(3Sum)问题时,先对数组进行排序是一个非常重要的步骤。排序不仅简化了问题的解决过程,还能帮助我们高效地处理重复元素和优化查找逻辑。以下是详细解释:

1. 排序的作用

(1)方便去重
  • 排序后,相同的元素会相邻排列。这样,我们可以通过简单的条件判断(如 nums[i] == nums[i - 1])来跳过重复的元素,避免重复解。

  • 例如,数组 [-1, -1, 0, 1, 2] 中,两个 -1 相邻,可以通过判断 nums[i] == nums[i - 1] 来跳过第二个 -1

(2)简化查找逻辑
  • 排序后,数组是有序的,可以利用 双指针法 来高效查找满足条件的三元组。

  • 双指针法的时间复杂度是 O(n)O(n),而哈希法的时间复杂度是 O(n2)O(n2),因此排序后使用双指针法可以显著提高性能。

(3)确保结果有序
  • 排序后,找到的三元组 (nums[i], nums[j], nums[k]) 也是有序的,这样可以避免重复解(如 [-1, 0, 1] 和 [0, -1, 1] 被视为不同的解)。


内层循环中,哈希表的作用是存储已经遍历过的 nums[j]。每次遍历到一个新的 nums[j] 时:

  • 先检查哈希表中是否存在 target = -nums[i] - nums[j]

    • 如果存在,说明 nums[i] + nums[j] + target = 0,即找到一个三元组。

  • 然后将当前的 nums[j] 加入哈希表,以便后续的 nums[j] 可以使用它来查找目标值。

nums[i] 是外层循环的固定值,它不会被用作目标值(target),因为 target 的定义是 -nums[i] - nums[j]

去重逻辑
  • 在外层循环中,通过 if (i > 0 && nums[i] == nums[i - 1]) continue; 跳过重复的 nums[i]

  • 在内层循环中,哈希表会自动处理重复的 nums[j],因为哈希表的特性是存储唯一的键。

  • const auto& triplet

    • auto自动推导变量类型。在这里,triplet 的类型是 const vector<int>&

    • const:表示 triplet 是只读的,不能修改。

    • &:表示引用,避免拷贝 vector<int>,提高效率。

2.双指针法

先排序,有一层for循环,定义指针i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

问题分析

  1. sum 的计算位置

    • 你在外层循环中计算了 sum = nums[i] + nums[left] + nums[right],但在内层循环中 left 和 right 会变化,sum 的值却没有更新,导致逻辑错误。

  2. 去重逻辑

    • 你在找到满足条件的三元组后,没有跳过重复的 nums[left] 和 nums[right],这可能导致重复解。(

      多加了 while (left < right && nums[right] == nums[right + 1]) right--; 这一行代码,其实就是把 需要执行的逻辑提前执行了,但并没有减少 判断的逻辑。

      最直白的思考过程,就是right还是一个数一个数的减下去的,所以在哪里减的都是一样的。

      所以这种去重 是可以不加的。 仅仅是 把去重的逻辑提前了而已。

      )

  3. set 的使用

    • 你使用 set<vector<int>> 来存储结果,虽然可以避免重复解,但效率较低,因为 set 的插入操作是 O(log⁡n)O(logn) 的。

  4. 边界条件

    • 你没有处理数组长度小于 3 的情况。

class Solution{
public:
    vector<vector<int>> threeSum(vector<int>& nums){
        sort(nums.begin(), nums.end());
        vector<vector<int>> out;
        if (nums.size() < 3) {
            return out;
        }
        for(int i = 0; i < nums.size(); i++){
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            int left = i + 1, right = nums.size() - 1;
            while(left<right){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum > 0){
                    right --;
                }
                if(sum < 0){
                    left ++;
                }
                if(sum == 0){
                    out.push_back({nums[i], nums[left], nums[right]});
                    right --;
                    left ++;
                }
            } 
        }
        return out;

    }
};

相关文章:

  • ruoyi框架接入kkFileView
  • 侯捷 C++ 课程学习笔记:深入理解C++内存管理与类对象构造全过程
  • 【极客时间】浏览器工作原理与实践-2 宏观视角下的浏览器 (6讲) - 2.6 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?
  • 第005文-模拟入侵网站实现0元购
  • µCOS-III从入门到精通 第八章(时间片调度)
  • 点云 基于法线的双边滤波原理和过程
  • LeetCode hot 100—二叉树的最大深度
  • 能量石[算法题]
  • YOLOv12 项目部署指南! 含报错解决
  • Flutter底层实现
  • Go学习笔记:基础语法3
  • 【由技及道】镜像星门开启:Harbor镜像推送的量子跃迁艺术【人工智障AI2077的开发日志010】
  • CSS+Html面试题(二)
  • python网络爬虫开发实战之爬虫基础
  • Unity自定义渲染管线(Scriptable Render Pipeline)架构设计与实现指南
  • netty中Future和ChannelHandler
  • Best practice-生产环境中加锁的最佳实践
  • Anaconda 部署 DeepSeek
  • Java 大视界 -- Java 大数据在智能政务公共服务资源优化配置中的应用(118)
  • Linux | Vim 鼠标不能右键粘贴、跨系统复制粘贴
  • 全球前瞻|特朗普19日将与俄乌总统分别通话,英国脱欧后首开英欧峰会
  • 美国恶劣天气已造成至少28人死亡
  • 以色列在加沙发起新一轮强攻,同步与哈马斯展开无条件谈判
  • 浙江美术馆馆长人民日报撰文:打开更辽阔的审美场域
  • 魔都眼|邮轮港国际帆船赛启动,120名中外选手展开角逐
  • 探秘多维魅力,长江经济带、珠三角媒体总编辑岳阳行启动