0
点赞
收藏
分享

微信扫一扫

算法练习-day6

哈希表

454. 四数相加 II

题意:给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  1. 0 <= i, j, k, l < n
  2. nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例:

算法练习-day6_哈希表

       思路:本题我自己的思路就是暴力求解,给出的数组个数有限,那么我们就用四个for循环求出每种排列组合,然后在等于0时++count,由于时间复杂度较高的原因,这样我想是通过不了的,因此也就没有写代码了;还有一种方式是使用unordered_map求解:我们可以将数组分成两大组,例如1和2一组,3和4一组,然后对12组和的所有情况进行出现次数统计,然后再遍历34组和的情况于12组和相加,若相加等于0,则说明该组数据就是元组,count+=12组和出现的次数,这样即排除了组和重复的问题,又大大缩短了时间

C++代码:

    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> map;
        for(auto m:nums1)//统计出数组1和2出现的次数
        {
            for(auto n:nums2)
            {
                map[m+n]++;
            }
        }
        int count=0;
        for(auto m:nums3)//这里相当于是让3和4数组的和于1和2数组的和相加
        {
            for(auto n:nums4)
            {
                int tmp=-(m+n);
                if(map.find(tmp)!=map.end())//若相加等于0,则说明四个数为元组,那么我们就累加1和2相加组合出现的次数
                {
                    count+=map[tmp];
                }
            }
        }
        return count;
    }

383. 赎金信

题意:给你两个字符串ransomNote和magazine,判断ransomNote能不能由 magazine里面的字符构成。如果可以,返回true;否则返回false。magazine 中的每个字符只能在ransomNote中使用一次。

示例:

算法练习-day6_双指针_02

       思路:本题我的思路是,先创建一个unordered_map作为仓库,用于存储我们可以使用的元素及其次数,也就是遍历magazine元素出现的次数;然后是遍历ransomNote,每构建一个部位就在仓库中减去相应元素的次数;最后我们在遍历整个仓库,看是否由元素次数小于0的情况,若有则ransomNote的构建我们无法满足,返回false;否则输出true

    bool canConstruct(string ransomNote, string magazine) {
        if(magazine.size()<ransomNote.size())//先简单判断一下,能使用的资源是否小于需要的资源
        {
            return false;
        }
        unordered_map<char,int> magaz;
        for(auto e:magazine)//统计出我们可以使用的元素和次数,称仓库
        {
            magaz[e]++;
        }
        for(auto e:ransomNote)//计数我们使用元素构造完ransomNote后,仓库的情况
        {
            magaz[e]--;
        }
        for(auto e:magaz)//遍历仓库,看资源是否使用错误
        {
            if(e.second<0)
            {
                return false;
            }
        }
        return true;
    }

15. 三数之和

题意:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

示例:

算法练习-day6_哈希表_03

       思路:本题我最开始的想法是往哈希表上考虑,但是思索了一下,发现哈希表其实并不能很好的解决,因为我们需要将三元组输入arr中,而哈希表只起到存储的作用,并没有标记的作用,因此该题放弃了哈希表的做法。之后我也想到过使用三层for循环暴力求解的方法,然后对三元组排序,在判断arr中是否有相同的三元组,在判断是否需要插入,但很明显,O(n^2)的时间复杂程度无法通过lettcode编译;

       其次就是使用双指针的方式,将两次循环遍历变成一次操作,我们先将数组排序,这也是为了之后更好的去重,然后让i作为单独的三元组之一进行遍历数组,然后在i到nums.size()之间定义两个指针,left和right,一个指向开头,一个指向结尾,即一个最小,一个最大,按照滑动窗口的思路:

  1. nums[i]+nums[left]+nums[right]<0时,表示窗口过小,left++
  2. nums[i]+nums[left]+nums[right]>0时,表示窗口过大,right--
  3. nums[i]+nums[left]+nums[right]==0时,输入三元组

这里我们需要注意两点:

  1. i的去重,当i等于i-1时,跳过本次寻找三元组
  2. left和right的去重,在我们找到三元组输入后,我们需要再次对left和right进行判断,因为i可能于left和right继续组成三元组,相同的left和right可能导致三元组重复,若left的后一个指针指向的值等于前一个left++,right同理,right--

暴力求解代码:

    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> arr;
        for(int i=0;i<nums.size();i++)
        {
            for(int j=i+1;j<nums.size();j++)
            {
                for(int t=j+1;t<nums.size();t++)
                {
                    if(nums[i]+nums[j]+nums[t]==0)
                    {
                        vector<int> tmp{nums[i],nums[j],nums[t]};
                        sort(tmp.begin(),tmp.end());//排序为了更好的去重
                        if(find(arr.begin(),arr.end(),tmp)==arr.end())//查找有没有相同的三元组
                        {
                            arr.push_back(tmp);
                        }
                    }
                }
            }
        }
        return arr;
    }

双指针代码:

    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());//先排除,这样也是为了更好的去重
        vector<vector<int>> arr;
        for(int i=0;i<nums.size();i++)
        {
            if(i>0&&nums[i]==nums[i-1])//当i的遍历出现重复,直接跳过
            {
                continue;
            }
            int left=i+1,right=nums.size()-1;//双指针,类似滑动窗口
            while(left<right)
            {
                if(nums[i]+nums[left]+nums[right]<0)//三者和小于0,说明和太小,left边界++
                {
                    left++;
                }
                else if(nums[i]+nums[left]+nums[right]>0)//三者和大于0,说明和太大,right边界--
                {
                    right--;
                }
                else//三者和相等为三元组
                {
                    vector<int> tmp{nums[i],nums[left],nums[right]};
                    arr.push_back(tmp);
                    while(left<right&&nums[left]==nums[left+1])//这里需要二次去重,因为除了i需要去重外,left和right也需要去重
                    {
                        left++;
                    }
                    while(left<right&&nums[right]==nums[right-1])
                    {
                        right--;
                    }
                    left++,right--;
                }
            }
        }
        return arr;
    }

18. 四数之和

题意:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  1. 0 <= a, b, c, d < n
  2. a、b、c 和 d 互不相同
  3. nums[a] + nums[b] + nums[c] + nums[d] == target

示例:

算法练习-day6_双指针_04

思路:本题思路于上一题非常类似,上一题我们使用双指针的方式,将时间复杂程度降低了一个层级。本题也一样,我们使用双指针将原本的n的四次方变为三次方

双指针代码:

    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());//排序,为了更好的去重和跳过不必要的过程
        vector<vector<int>> arr;
        for (int i = 0; i<nums.size(); i++)
        {
            if (i > 0 && nums[i] == nums[i - 1])//排除i重复的情况
                continue;
            for (int j = i + 1; j < nums.size(); j++)
            {
                if(j>i+1&&nums[j]==nums[j-1])//排除j重复的情况
                    continue;
                int left = j + 1, right = nums.size() - 1;
                while (left < right)
                {
                    if ((long)nums[i]+nums[j]+nums[left]+nums[right] < target)//这里需要强转,防止越界
                        left++;
                    else if ((long)nums[i]+nums[j]+nums[left]+nums[right] > target)
                        right--;
                    else
                    {
                        arr.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});//插入的都是不重复的
                        while(left<right&&nums[left]==nums[left+1])//排除left和right重复的情况
                            left++;
                        while(left<right&&nums[right]==nums[right-1])
                            right--;
                        left++, right--;
                    }
                }
            }
        }
        return arr;
    }


举报

相关推荐

ARM day6

作业day6

Python Day6

Day6 FileUpload

C++ DAY6

JAVA学习day6

Linux学习-DAY6

0 条评论