Datawhale模拟面试每日打卡 day-14 15. 三数之和

本文介绍了两种解决LeetCode 15题《三数之和》的方法。第一种是哈希算法+双重for循环,通过排序和哈希表减少时间复杂度至O(n^2),但实现时需要注意去重细节。第二种方法是双指针法,更高效且易于理解和实现,通过排序后固定一个元素,使用双指针从两侧向中间查找,时间复杂度同样为O(n^2)。文章强调了在面试中选择合适解法的重要性。
摘要由CSDN通过智能技术生成

在这里插入图片描述

解题思路:https://leetcode-cn.com/problems/3sum/solution/15-san-shu-zhi-he-ha-xi-fa-shuang-zhi-zhen-fa-xi-2/

  • 暴力破解:三重for循环,去重部分可以采用unordered_set进行存储结果,然后再转成vector进行输出
  • hash算法+双重for循环,注意去重设置
  • 双指针

作者:carlsun-2
链接:https://leetcode-cn.com/problems/3sum/solution/15-san-shu-zhi-he-ha-xi-fa-shuang-zhi-zhen-fa-xi-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法一 : hash算法+双重for循环

class Solution {
public:
// 哈希算法+双重for循环,类似两数之和来解决
// 正常采用暴力的三重for循环来进行扫描时间复杂度为O(n^3),通过哈希算法unordered_set可以省掉一层for循环则时间复杂度为O(n^2)
// 但是要注意去除重复的条件设置,这个是难点,很容易出现问题
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        // 先进行升序处理,对后期的去重有很大帮助
        sort(nums.begin(),nums.end());
        for(int i = 0;i<nums.size();++i){
            if(nums[i] > 0)continue;
            // 这里i>0是防止nums[i-1]越界,num[i]==nums[i-1]是为了去重第一个元素,例如nums排序后[-3,1,1,2,3],如果不进行去重判断,那么最终结果[-3,1,2],[-3,1,2]
            // 第一组的1是索引为1的值,第二组的1是索引为2的值
            if(i > 0 && nums[i]==nums[i-1])continue;
        
            // 创建hashset来省略一层for循环
            unordered_set<int>uset;
            for(int j = i + 1;j<nums.size();++j ){
                // [-4 2 2 2 3 3 4 ]这种就就只能用nums[j-1]==nums[j-2]来约束三个连续重复条件
                if(j > i + 2 && nums[j]==nums[j-1] && nums[j-1]==nums[j-2])continue;
                int c = 0 - (nums[i] + nums[j]);
                if(uset.find(c) != uset.end()){
                    res.push_back({nums[i],nums[j],c});
                    // 去除第三个元素,如果不去除情况[-2,0,0,2,2],的到的结果[[-2,2,0],[-2,2,0]],其中第一个2是索引为3,第二个2是索引为4
                    uset.erase(c);
                }else{
                    uset.insert(nums[j]);
                }
            }
        }
        return res;
    }
};

其实这道题目使用哈希法并不十分合适,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是O(n^2),也是可以在leetcode上通过,但是程序的执行时间依然比较长 。而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是O(n^2),也是可以在leetcode上通过,但是程序的执行时间依然比较长 。

接下来我来介绍另一个解法:双指针法,这道题目使用双指针法 要比哈希法高效一些。其实双指针

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) {
                return result;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
                /*
                while (right > left && nums[right] == nums[right - 1]) right--;
                while (right > left && nums[left] == nums[left + 1]) left++;
                */
                if (nums[i] + nums[left] + nums[right] > 0) {
                    right--;
                } else if (nums[i] + nums[left] + nums[right] < 0) {
                    left++;
                } else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重逻辑应该放在找到一个三元组之后
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

                    // 找到答案时,双指针同时收缩
                    right--;
                    left++;
                }
            }

        }
        return result;
    }
};
void welcome() { printf("**********欢迎使用管理系统*************\n"); //以不同的角色进行登录系统 printf("1.管理员身份登录\n"); printf("2.普通用户学生登录\n"); printf("3.退出\n"); printf("******************************************\n"); } //管理员的菜单界面 void mangeview() { printf("**********欢迎管理员登录********\n"); printf("-----------------1.注册学生信息------------------\n"); printf("-----------------2.输出学生信息------------------\n"); printf("-----------------3.删除学生信息------------------\n"); printf("-----------------4.修改学生信息------------------\n"); printf("-----------------5.查询学生信息------------------\n"); printf("-----------------6.学生请假和补卡------------------\n"); printf("-----------------7.排序(姓名、学号、打卡次数)--\n"); printf("-----------------8.考勤数据统计------------------\n"); printf("-----------------9.返回------o( ̄ヘ ̄o#)----\n"); //........ printf("********************************\n"); } //普通用户界面 //普通用户界面提示 void comuser(){ printf("************欢迎学生登录************\n"); printf("-----------------1.录入学生信息------------------\n"); printf("-----------------2.输出学生信息------------------\n"); printf("-----------------3.查询学生信息------------------\n"); printf("-----------------4.排序(姓名、学号、打卡次数)--\n"); printf("-----------------5.学生请假和补卡------------------\n"); printf("-----------------6.返回------o( ̄ヘ ̄o#)----\n"); printf("********************************************\n"); } 这个代码怎么描述,运用了什么函数
06-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值