力扣刷题总结 哈希表(2)

博客主页: A_SHOWY
系列专栏:力扣刷题总结录 数据结构  云计算

1.两数之和 easy

map哈希,因为要返回下标

15.三数之和 mid 哈希很难,因为要考虑去重,双指针法更优
18.四数之和 mid 和三数之和相似,使用双指针,注意剪枝区别

        哈希表的第二部分主要总结几个使用哈希表非常复杂的问题,当哈希表需要考虑的去重问题非常复杂的时候,双指针法是更优的选择 。

(1)1.两数之和

1. 两数之和icon-default.png?t=N7T8https://leetcode.cn/problems/two-sum/核心思想是把遍历的数值存放到map里面(因为是要返回数组下标),具体思路可以看下面这个文章。力扣刷题总结 哈希表(1)-CSDN博客

class Solution {
public:
    vector twoSum(vector& nums, int target) {
    unordered_map map;
    for(int i = 0; i < nums.size(); i++)
    {
        int s = target - nums[i];
        auto iter = map.find(s);
        if(iter != map.end()) return{iter -> second,i};
        else map.insert(pair(nums[i],i));
    }
    return {};
    }
};

(2)15.三数之和

15. 三数之和icon-default.png?t=N7T8https://leetcode.cn/problems/3sum/

方法一:哈希法

两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组,要去重,a去重,b去重,a+b也要去重,细节特别多。

class Solution {
public:
    vector> threeSum(vector& nums) {
vector> result;
sort(nums.begin(),nums.end());
for(int i = 0; i < nums.size(); i++)
{
    if(nums[i] > 0) return result;
//对a去重 
    if(i > 0 && nums[i] == nums[i-1]) {continue;}
    unordered_set set;
    for(int j = i+1; j < nums.size(); j++)
    {
        if(j > i+2 && nums[j] == nums[j-1] && nums[j-1] == nums[j-2]) {continue;}
        int c = 0 -nums[i] - nums[j];
        if(set.find(c) != set.end()) {result.push_back({nums[i],nums[j],c});
        set.erase(c);}//去重c
        else {set.insert(nums[j]);}
    }
}
return result;
    }
};

可以看出超出了时间限制,而且里面的去重操作非常繁琐

1.首先是对a去重没什么坑,从对b去重开始,对b去重,一般人可能想到我只需要判断和前一个不一样不就行了吗,为什么还要和前两个不一样 

if(j > i+2 && nums[j] == nums[j-1] && nums[j-1] == nums[j-2]) 

这就是这道题麻烦的地方,要考虑一组数前面是{0,0,0......}的情况

i   j   i+2   i+3
0   0   0     1    2    3

打个比方形如如上这种对应关系,假如我们只判断i+2等于i+1,那就把0,0,0这种情况漏过去了。 

i   j   i+2   i+3
0   0   0     0    2    3

 那假如是这种情况i+3也是0了,那么就0,0,0重复了才需要删掉.

 如果我们只判断nums[j] == nums[j-1]例如下面的样例就会错误,当连续三个的时候,就能判断不会重复,这很难想到!

力扣刷题总结 哈希表(2)_第1张图片

方法二:双指针法(更优)

力扣刷题总结 哈希表(2)_第2张图片

需要排序,因为输出的不需要输出下标,看上面的图,我们的目标是求三数之和,同时要满足不能出现相同的三元组,首先需要对数组进行排序。满足a+b+c = 0的话让i从第一位开始代表a,left在i+1,right在末尾。核心思路是,首先是i进行遍历,如果nums【i】+nums【left】+ nums【right】<0 的时候,就让left++,大于0的时候就right--,当等于0的时候就输出,但是这道题目尽管在使用双指针法的时候有很多很细节的点需要注意。

class Solution {
public:
    vector> threeSum(vector& nums) {
         vector> result;
         sort(nums.begin(),nums.end());//先进行整体排序
         for(int i = 0; i < nums.size(); i++)
         {
             if(nums[i] > 0) return result;
//第一个去重,去除相同的三元组
        if(i > 0 && nums[i] == nums[i - 1]) {continue;}//很重要需要判断i>0,不能颠倒顺序
        int left = i + 1;
        int right = nums.size() - 1;
        while(left < right)//为什么是等于
    //这里不能进行第二次去重,因为会把{0,0,0}这种情况考虑不到
    {
        if(nums[i] + nums[left] + nums[right] > 0) right --;
       else if(nums[i] + nums[left] + nums[right] < 0) left ++;
       else{
           result.push_back(vector{nums[i],nums[left],nums[right]});//使用了push_back方法向名为result的vector容器中添加一个新的元素,这个新元素是一个vector类型的对象。
//此时进行第二次去重
     while(nums[right] == nums[right - 1] && right > left) {right--;}
     while(nums[left] == nums[left + 1] && right > left) {left++;}
//找到答案时,双指针同时收缩
    right--;
    left++;
         }
         }
    }
    return result;
}
};

1.首先是第一次的去重(a的去重)。为什么使用//1而不用//2

nums[i] == nums[i - 1]//1
nums[i] == nums[i + 1]//2

我们考虑如下情况,当这个数组是{-1,-1,2}的时候,使用//2我们判断第二个元素和第一个一样就会误把第二个元素删掉,但是我们的目的是删除相同的三元组而不是删除一个三元组中相同的元素。所以我们和前面的元素比较,使用过了,就可以安心的去掉。但是还有一个注意点,我们使用nums【i-1】了以后要注意判断i>0,而且要写在去重判断的前面,if判断&&是有顺序的。

2.在进入主题部分时,为什么是

while(left < right)

而不是

while(left <= right)

因为我们考虑,当left=right,b和c重合了,我们讨论b和c的意义就没有了,b和c公用一个元素,不满足题意了,所以不能加上等于。

3.第二次去重(b和c的去重)为什么不能在前面刚开始判断while就进行去重,也就是这样

while(left < right)//为什么是等于
    {

 while(nums[right] == nums[right - 1] && right > left) {right--;}
 while(nums[left] == nums[left + 1] && right > left) {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{nums[i],nums[left],nums[right]});
    

    right--;
    left++;
         }
         }

因为我们考虑{0,0,0}的这种情况,会把这种情况错误的去掉。同时注意push_back操作,使用了push_back方法向名为result的vector容器中添加一个新的元素,这个新元素是一个vector类型的对象。

 (3)18.四数之和

18. 四数之和icon-default.png?t=N7T8https://leetcode.cn/problems/4sum/这个题目和上一个题目相比只变了两个地方,一个是三数变成了四数,另外一个是上一题让三数之和的目标值等于0,而本题让手动输入一个target,让四个数之和等于target,所以说使用双指针的思路是没有问题的和上一个题目大同小异,两个for循环遍历a和b,c和d用left和right双指针去移动,如果使用哈希法,将更为困难。值得注意的点有以下几个。

class Solution {
public:
    vector> fourSum(vector& nums, int target) {
     vector>  result;
     sort(nums.begin(),nums.end());
     for(int k = 0; k < nums.size(); k++)
     {
         if(nums[k] > target && nums[k] >= 0) {break;}//剪枝操作
         if(k > 0 && nums[k] == nums[k-1]) {continue;}//对a去重

         for(int i = k+1; i < nums.size(); i++)
         {
             if(nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {break;}
             if(i > k+1 && nums[i] == nums[i-1]) {continue;}

             int left = i + 1;
             int right = nums.size() -1;
             while(left < right)
             {
                 if((long)nums[i] + nums[k] + nums[left] + nums[right] < target) left ++;
                 else if((long)nums[i] + nums[k] + nums[left] + nums[right] > target) right --;
                 else{
                     result.push_back(vector{nums[k],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--;
                 }
            
             }
         }
     }
     return result;
    }
};

1.第一个就是剪枝操作有区别,我们日常思维考虑的是数字相加,越加越大,其实不然,只有在都是正数的时候,正数和正数相加才会越加越大,而负数会越加越小。

2.去重的操作和上一题大同小异基本是一样的。

3.在进行四数相加的时候有可能会超界,所以要声明long,调试跑样例的时候能查出来。

(4)哈希表使用选择问题

三种:1数组 2 set(集合)3map(映射)

  • 对于set

1.当使用set解决哈希问题的时候,优先使用unordered_set,因为查询和增删效率是最优的(需要去重的时候)

2.如果需要集合有序,使用set

3.如果要求不仅有序还要有重复数据的话,那么就用multiset。(需要保留重复数据)

  • 对于map

1.map 是一个key value 的数据结构,map中,对key是有限制,对value没有限制

2.其他有序和重复细节和set是一样的

你可能感兴趣的:(力扣刷题总结录,leetcode,算法,哈希算法,c++,哈希表)