算法 哈希表2 || 第454题.四数相加II || 383. 赎金信 || 第15题. 三数之和 || 第18题. 四数之和

第454题.四数相加II

和两数之和很相似,直接把四个数组分为两个大组。A+B的每一种可能以及出现的次数放进map,再去找 0-(c+d),每找到一次count计数,要加上0-(c+d)这个k对应的value值,而不是++。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> umap;
        int count = 0;
        for(int a : nums1){
            for(int b : nums2){
                umap[a+b]++;
            }
        } 
        for(int c : nums3){
            for(int d : nums4){
               auto it = umap.find(0 - (c + d));
               if(it != umap.end()) count += umap[0 - (c + d)];
            }
        } 
        return count;
    }
};

383. 赎金信

这道题和 242.有效的字母异位词 非常像。几乎就是一模一样

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        if(magazine.size() < ransomNote.size()) return false;
        int set[26] = {0};
        for(char c : ransomNote){
            set[c - 'a']++;
        }
        for(char c : magazine){
            set[c - 'a']--;
        }
        for(int a : set){
            if(a > 0) return false;
        }
        return true;
    }
};

第15题. 三数之和(很不熟练 多写几遍)

  • 明确题意:题目不仅要求,一组的三元素下标不能相同,并且!两组不能重复!
    四数之和II,并不要求数组中的元素不能被使用两次,所以可以直接分组,然后用find(target-(c+d)) 的方式来做。但是三数之要求:数组中的元素,在每一组符合条件的答案中只能出现一次。两数之和只会出现一个答案,并且先查询再遍历的方式可以确保元素只被使用一次。但是三数之和如果使用find(target-(a+b))的方式,去重会变得异常麻烦。所以考虑使用双指针
    先排序,一次for循环,right指向数组尾端,left指向 i+1。
  • 去重方法:
    下标的去重,保证一组答案里没有被重复使用的元素非常容易,使用双指针和正确的边界条件就可以做到。本题的难点在于:每一组答案都互不重复。
    例如:
    输入: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] 。
    注意,输出的顺序和三元组的顺序并不重要。
    以下为具体的去重方法
    • [ 1 ] 首先是a的去重:
      虽然已经排序过了,但是要考虑{-1,-1,-1,2}这种情况,如果a的去重上来就让index = 2,就会漏掉 -1 -1 2这一组答案。三个相同的数相加等于0的情况只有000,所以最多保留两个相同的数即可。即使前两个数是0 0 ,left = i+1 nums[left] = 0的时候 三个0的情况就被考虑进来了
	// 错误去重a方法,将会漏掉-1,-1,2 这种情况
	 
	   if (nums[i] == nums[i + 1]) {
	       continue;
	   }
 
  • [ 2 ] b c的去重:
    如果去重放在while一进来的地方:
    // 去重复逻辑如果放在这里,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++;
    */
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> vec;
        for(int i = 0; i < nums.size(); i++){
            //如果排序后第一个元素就大于0 
            if(nums[i] > 0) return vec;
            //对a去重
            //不满足这个条件就直接到left那一步,满足条件就i++
            //加一个i>0是为了排除 i=0 这个情况出现nums[-1]
            //这样保证几个重读元素是从头被遍历的 不漏掉{-1 -1 2}这种情况
           if(i > 0 && nums[i] == nums[i - 1]) continue; 
            int left = i + 1;
            int right = nums.size()-1;
            while(left < right){
                if(nums[left] + nums[right] + nums[i] > 0) right--;
                else if(nums[left] + nums[right] + nums[i] < 0) left++;
                else if(nums[left] + nums[right] + nums[i] == 0) {
                    vec.push_back({nums[left] , nums[right] , nums[i]});
                    //找到以后才需要去重
                    while(right > left && nums[left] == nums[left+1]) left++;
                    while(right > left && nums[right] == nums[right-1]) right--;
                    //此时的left和right是当前的答案 上面只是做了去重,
                    //比如{3,3,2,4,5,5},只是从index 0 5到了1 4 而不是2 3
                    //所以需要向内收缩
                    left++;
                    right--;
                }
            }
        }
        return vec;
    }
};

自己写的暴力解法,三次for循环,时间复杂度是O(n3)

class Solution {
	public:
		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) return res;
				while (i > 0 && i < nums.size()&& nums[i - 1] == nums[i]) i++;
				for (int j = i + 1; j < nums.size(); j++) {
					if (nums[i] + nums[j] > 0) break;
					while (j > i + 1 && j < nums.size() && nums[j - 1] == nums[j])j++;

					for (int k = j + 1; k < nums.size(); ) {
						if (nums[i] + nums[j] + nums[k] < 0) k++;
						if (nums[i] + nums[j] + nums[k] > 0) break;
						
						if (nums[i] + nums[j] + nums[k] == 0) {
							res.push_back({ nums[i],nums[j],nums[k] });
							k++;
						}
						while (k > j + 1 && k < nums.size() && nums[k - 1] == nums[k]) k++;
					}
				}
			}
		}
	};

第18题. 四数之和

跟上一题三数之和相比,就是多了一层for循环。其他去重思路基本一致,和三数之和一样,将时间复杂度降低了n,将原本暴力O(n4)的解法,降为O(n3)的解法

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> result;

        for(int i = 0; i < nums.size(); ++i ){
            if(nums[i] > target && nums[i] >= 0) break;
            if(i > 0 && nums[i - 1] == nums[i]) continue;
            for(int j = i+1; j < nums.size(); ++j){
                if(nums[i] + nums[j] > target && nums[i] + nums[j] >= 0) break;
                if(j > i+1 && nums[j] == nums[j-1]) continue;
                int left = j+1;
                int right = nums.size() - 1;
                while(left < right){
                    if(left < right && (long)nums[i] + nums[j] + nums[left] + nums[right] > target) right--;
                    else if(left < right && (long)nums[i] + nums[j] + nums[left] + nums[right] < target) left++;
                    else {
                        result.push_back({nums[i] , nums[j] , 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;
    }
};

你可能感兴趣的:(算法,散列表,leetcode)