2、LeetCode--查找(set map)

二、查找(set / map)

第1部分

1.1 LeetCode 349. 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

说明:

  • 输出结果中的每个元素一定是唯一的。
  • 我们可以不考虑输出结果的顺序。

解法一:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> record;
        for (int i = 0; i < nums2.size(); ++i) {
            record.insert(nums2[i]);
        }
        unordered_set<int> resultSet;
        for (int i = 0; i < nums1.size(); ++i) {
            if (record.find(nums1[i])!=record.end())
                resultSet.insert(nums1[i]);
        }
        vector<int> resultVector;
        for(unordered_set<int>::iterator 
                      iter=resultSet.begin();iter!=resultSet.end();iter++){
            resultVector.push_back(*iter);
        }
        return resultVector;
    }
};

解法二:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> record(nums2.begin(),nums2.end());
        unordered_set<int> resultSet;
        for (int i = 0; i < nums1.size(); ++i) {
            if (record.find(nums1[i])!=record.end())
                resultSet.insert(nums1[i]);
        }
        return vector<int>(resultSet.begin(),resultSet.end());
    }
};

1.2 LeetCode 350. 两个数组的交集 II

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]

说明:

  • 输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。

  • 我们可以不考虑输出结果的顺序。
    进阶:

  • 如果给定的数组已经排好序呢?你将如何优化你的算法?

  • 如果 nums1 的大小比 nums2 小很多,哪种方法更优?

  • 如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

解法一:

class Solution {
public:
    vector<int> intersect(vector<int> &nums1, vector<int> &nums2) {
        unordered_map<int, int> record;
        for (int i = 0; i < nums1.size(); ++i) {
            record[nums1[i]]++;
        }
        vector<int> resultVec;
        for (int i = 0; i < nums2.size(); ++i) {
            if (record[nums2[i]] > 0) {
                resultVec.push_back(nums2[i]);
                record[nums2[i]]--;
            }
        }
        return resultVec;
    }
};

解法二:

class Solution {
public:
    vector<int> intersect(vector<int> &nums1, vector<int> &nums2) {
        unordered_map<int, int> record;
        for (int i = 0; i < nums1.size(); ++i) {
            if (record.find(nums1[i]) == record.end())//查找
                record.insert(make_pair(nums1[i], 1));//插入
            else
                record[nums1[i]]++;
        }
        vector<int> resultVec;
        for (int i = 0; i < nums2.size(); ++i) {
            if (record.find(nums2[i]) != record.end() && record[nums2[i]] > 0) {
                resultVec.push_back(nums2[i]);
                record[nums2[i]]--;
                if (record[nums2[i]] == 0)
                    record.erase(nums2[i]);//彻底删除
            }
        }
        return resultVec;
    }
};

1.3 LeetCode 242. 有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

说明:

  • 你可以假设字符串只包含小写字母。

进阶:

  • 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

解法一:

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.size()!=t.size())
            return false;
        unordered_map<char, int> record;
        for (int i = 0; i < s.size(); ++i) {
            record[s[i]]++;
        }
        for (int i = 0; i < t.size(); ++i) {
            if (record[t[i]] > 0)
                record[t[i]]--;
            else
                return false;
        }
        return false;
    }
};

1.4 LeetCode 202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。

如果 n 是快乐数就返回 True ;不是,则返回 False 。

示例:

输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

解法一:

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> record;
        int sum;
        while(true){
            sum=0;
            while (n != 0) {
                int i=n%10;
                sum+=i*i;
                n=n/10;
            }
            if(sum==1)
                return true;
            if(record.find(sum)!=record.end())
                return false;
            else
                record.insert(sum);
            n=sum;
        }
    }
};

1.5 LeetCode 290. 单词规律

给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。

这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。

示例1:

输入: pattern = "abba", str = "dog cat cat dog"
输出: true

示例 2:

输入:pattern = "abba", str = "dog cat cat fish"
输出: false

示例 3:

输入: pattern = "aaaa", str = "dog cat cat dog"
输出: false

示例 4:

输入: pattern = "abba", str = "dog dog dog dog"
输出: false

说明:

  • 你可以假设 pattern 只包含小写字母, str 包含了由单个空格分隔的小写字母。

解法一:

class Solution {
public:
    bool wordPattern(string pattern, string s) {
        istringstream is(s);
        string str;
        vector<string> vec;
        while (is >> str) {
            vec.push_back(str);
        }
        if (vec.size() != pattern.size())
            return false;

        unordered_map<char, string> record1;
        unordered_map<string, char> record2;
        for (int i = 0; i < pattern.size(); ++i) {
            if (record1.find(pattern[i]) == record1.end()) {
                if (record2.find(vec[i]) != record2.end())
                    return false;
                record1.insert(make_pair(pattern[i], vec[i]));
                record2.insert(make_pair(vec[i], pattern[i]));
            } else {
                if (record1[pattern[i]] != vec[i])
                    return false;
            }
        }
        return true;
    }
};

1.6 LeetCode 205. 同构字符串

给定两个字符串 s 和 t,判断它们是否是同构的。

如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。

所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。

示例 1:

输入: s = "egg", t = "add"
输出: true

示例 2:

输入: s = "foo", t = "bar"
输出: false

示例 3:

输入: s = "paper", t = "title"
输出: true

说明:

  • 你可以假设 s 和 t 具有相同的长度。

解法一:

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        unordered_map<char, char> record1;
        unordered_map<char, char> record2;
        if (s.size() != t.size())
            return false;
        for (int i = 0; i < s.size(); i++) {
            if (record1.find(s[i]) != record1.end()) {
                if (record1[s[i]] != t[i])
                    return false;
            } else {
                if (record2.find(t[i]) != record2.end())
                    return false;
                record1.insert(make_pair(s[i], t[i]));
                record2.insert(make_pair(t[i], s[i]));
            }
        }
        return true;
    }
};

1.7 LeetCode 451. 根据字符出现频率排序

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

示例 1:

输入:
"tree"

输出:
"eert"

解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。

示例 2:

输入:
"cccaaa"

输出:
"cccaaa"

解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。

示例 3:

输入:
"Aabb"

输出:
"bbAa"

解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。

解法一:map排序

class Solution {
public:
    string frequencySort(string s) {
        unordered_map<char, int> record;
        for (int i = 0; i < s.size(); ++i) {
            record[s[i]]++;
        }
        vector<pair<char, int>> vec;//不是 vector<make_pair<char,int>> vec;
        for (const auto &e:record) {
            vec.push_back(e);
        }
		//sort第三个参数是函数方法,类似于注释中的方法judge
        sort(vec.begin(), vec.end(),
             [](const pair<char, int> &p1, const pair<char, int> &p2) 
             		{ return p1.second > p2.second; });
        
        string str;
        for (const auto &e:vec) {
            str += string(e.second, e.first);
        }
        return str;
    }
    /*bool judge(const pair<char, int> a, const pair<char, int> b) {
        if (a.second == b.second)
            return a.first < b.first;
        else
            return a.second > b.second;
    }*/
};

解法二:优先队列

class Solution {
public:
    string frequencySort(string s) {
        unordered_map<char, int> record;
        for (int i = 0; i < s.size(); ++i) {
            record[s[i]]++;
        }
        priority_queue<pair<int,char>> queue;
        for(const auto&e:record) {
            //返回pair的比较结果,先按照pair的first元素降序,first元素相等时,再按照second元素降序:
            queue.push({e.second, e.first});
        }
        string str;
        while (!queue.empty()) {
            auto e=queue.top();
            queue.pop();
            str+=string(e.first,e.second);
        }
        return str;
    }
};

注:

  1. priority_queue本质是一个堆。
  2. 比较方式默认用operator<,所以如果把后面2个参数缺省的话,优先队列就是大顶堆(降序),队头元素最大。
  3. 返回pair的比较结果,先按照pair的first元素降序,first元素相等时,再按照second元素降序:

第二部分

2.1 LeetCode 1. 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解法一:

class Solution {
public:
    vector<int> twoSum(vector<int> &nums, int target) {
        unordered_map<int, int> record;
        for (int i = 0; i < nums.size(); ++i) {
            int elem = target - nums[i];
            if (record.find(elem) != record.end()) {
                int ans[2] = {record[elem], i};
                return vector<int>(ans, ans + 2);
            }
            record.insert(make_pair(nums[i], i));
        }
        return {};
    }
};

2.2 LeetCode 15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

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

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

解法一:排序+双指针

class Solution {
public:
    vector<vector<int>> threeSum(vector<int> &nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        if (nums.size() < 3)return ans;

        for (int i = 0; i < nums.size() - 2; ++i) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int a;
            int l = i + 1;
            int r = nums.size() - 1;
            a = nums[i];  //三元组中最小是元素
            if (a > 0) return ans;
            while (l < r) {
                if (l != i + 1 && nums[l] == nums[l - 1]) { //去重
                    l++;
                    continue;
                }
                if (r != nums.size() - 1 && nums[r] == nums[r + 1]) { //去重
                    r--;
                    continue;
                }
                if (nums[l] + nums[r] + a == 0) {
                    ans.push_back(vector<int>{a, nums[l], nums[r]});
                    l++;
                    r--;
                } else if (nums[l] + nums[r] + a >= 0) {
                    r--;
                } else {
                    l++;
                }
            }
        }
        return ans;
    }
};

方法二:排序+哈希

略。。。。。。

2.3 LeetCode 16. 最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

提示:

  • 3 <= nums.length <= 10^3
  • -10^3 <= nums[i] <= 10^3
  • -10^4 <= target <= 10^4

解法一:排序+双指针

class Solution {
public:
    int threeSumClosest(vector<int> &nums, int target) {
        sort(nums.begin(), nums.end());
        int ans = nums[0] + nums[1] + nums[2];
        for (int i = 0; i < nums.size() - 2; ++i) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int l = i + 1;
            int r = nums.size() - 1;
            while (l < r) {
                int sum = nums[i] + nums[l] + nums[r];
                if (abs(ans - target) > abs(sum - target)) {
                    ans = sum;
                }
                if (sum == target)
                    return ans;
                else if (sum > target) {
                    r--;
                } else {
                    l++;
                }
            }
        }
        return ans;
    }
};

方法二:排序+哈希

略。。。。。。

2.4 LeetCode 49. 字母异位词分组

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

说明:

  • 所有输入均为小写字母。
  • 不考虑答案输出的顺序。

解法一:

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string> &strs) {
        vector<vector<string>> ans;
        unordered_map<string, vector<string>> record;
        for (const auto str:strs) {
            string s = str;
            sort(s.begin(), s.end());
            record[s].push_back(str);
        }
        for (auto elem = record.begin(); elem != record.end(); elem++) {
            ans.push_back(elem->second);
        }
        return ans;
    }
};

2.5 LeetCode 447. 回旋镖的数量

给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。

找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。

示例:

输入:
[[0,0],[1,0],[2,0]]

输出:
2

解释:
两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]

解法一:

class Solution {
public:
    int dis(const pair<int, int> &point1, const pair<int, int> &point2) {
        //避免浮点误差
        return (point1.first - point2.first) * (point1.first - point2.first)
               + (point1.second - point2.second) * (point1.second - point2.second);
    }
    int numberOfBoomerangs(vector<vector<int>> &points) {
        int ans = 0;
        for (int i = 0; i < points.size(); ++i) {
            unordered_map<int, int> record;//**
            for (int j = 0; j < points.size(); ++j) {
                if (i != j)
                    record[dis(make_pair(points[i][0], points[i][1]),
                               make_pair(points[j][0], points[j][1]))]++;
            }
            for (const auto elem:record) {
                if (elem.second >= 2)
                    ans += elem.second * (elem.second - 1);//**
            }
        }
        return ans;
    }
};

2.6 LeetCode *149. 直线上最多的点数

给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。

示例 1:

输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
|        o
|     o
|  o  
+------------->
0  1  2  3  4

示例 2:

输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
|  o
|     o        o
|        o
|  o        o
+------------------->
0  1  2  3  4  5  6

解法一:

class Solution {
private:
    //**最大公约数
    int gcd(int m, int n) {
        return n == 0 ? m : gcd(n, m % n);
    }
    //**用数对(分数)代替斜率,避免丢失精度
    pair<int, int> getSlope(const vector<int> &p1, const vector<int> &p2) {
        int x = p1[0] - p2[0];
        int y = p1[1] - p2[1];
        if (x == 0) return pair<int, int>(0, p1[1]);
        if (y == 0) return pair<int, int>(p1[0], 0);
        int n = gcd(x, y);
        return pair<int, int>(x / n, y / n);
    }

public:
    int maxPoints(vector<vector<int>> &points) {
        int ans = 0;
        for (int i = 0; i < points.size(); ++i) {
            int same_count = 1;
            int max_count = 0;
            map<pair<int, int>, int> record;
            for (int j = i + 1; j < points.size(); ++j) {
                if (points[j][0] == points[i][0] && points[j][1] == points[i][1])
                    same_count++;
                else {
                    //一次循环后只有record[getSlope(points[i], points[j])]变化,
                    //可以直接获取最大值****
                    max_count = max(max_count, ++record[getSlope(points[i], points[j])]);
                }
            }
            ans = max(ans, max_count + same_count);
        }
        return ans;
    }
};

第三部分

3.1 LeetCode 219. 存在重复元素 II

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例 1:

输入: nums = [1,2,3,1], k = 3
输出: true

示例 2:

输入: nums = [1,0,1,1], k = 1
输出: true

示例 3:

输入: nums = [1,2,3,1,2,3], k = 2
输出: false

解法一:滑动窗口(双指针)+ 查找表(set)

class Solution {
public:
    bool containsNearbyDuplicate(vector<int> &nums, int k) {
        unordered_set<int> record;
        int i = 0;
        int j = 0; //(i,j]
        if (nums.size() == 0)
            return false;
        record.insert(nums[i]);
        while (j + 1 < nums.size()) {
            j++;
            if (j - i > k) {
                record.erase(nums[i]);
                i++;
            }
            if (record.find(nums[j]) != record.end())
                return true;
            else
                record.insert(nums[j]);
        }
        return false;
    }
};

解法二:滑动窗口(单指针)+ 查找表(set)

class Solution {
public:
    bool containsNearbyDuplicate(vector<int> &nums, int k) {
        unordered_set<int> record;
        for (int i = 0; i < nums.size(); ++i) {
            if (record.size() == k + 1)
                record.erase(nums[i - k - 1]);

            if (record.find(nums[i]) != record.end())
                return true;
            else
                record.insert(nums[i]);
        }
        return false;
    };
};

3.2 LeetCode 217. 存在重复元素

给定一个整数数组,判断是否存在重复元素。

如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1:

输入: [1,2,3,1]
输出: true

示例 2:

输入: [1,2,3,4]
输出: false

示例 3:

输入: [1,1,1,3,3,4,3,2,4,2]
输出: true

解法一:查找表

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> record;
        for (int i = 0; i < nums.size(); ++i) {
            if(record.find(nums[i])!=record.end())
                return true;
            else
                record.insert(nums[i]);
        }
        return false;
    }
};

3.2 LeetCode 220. 存在重复元素 III

在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。

如果存在则返回 true,不存在返回 false。

示例 1:

输入: nums = [1,2,3,1], k = 3, t = 0
输出: true

示例 2:

输入: nums = [1,0,1,1], k = 1, t = 2
输出: true

示例 3:

输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false

解法一:查找表+low_bound(效率极低)

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int> &nums, int k, int t) {
        set<long> record;//
        for (int i = 0; i < nums.size(); ++i) {
            if (record.size() == k + 1)
                record.erase(nums[i - k - 1]);
            if (record.lower_bound((long)nums[i] - t) != record.end()
                && *record.lower_bound((long)nums[i] - t) <= (long)nums[i] + t) {
                return true;
            } else {
                record.insert((long)nums[i]);
            }
        }
        return false;
    }
};

注:

  • set、map等内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。即,set中插入数据时就有顺序。
  • record.lower_bound(k),查找第一个<=k的数,即是,最接近K的数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值