哈希表

设计哈希集合

不使用任何内建的哈希表库设计一个哈希集合

具体地说,你的设计应该包含以下的功能

  • add(value):向哈希集合中插入一个值。
  • contains(value) :返回哈希集合中是否存在这个值。
  • remove(value):将给定值从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。


示例:

MyHashSet hashSet = new MyHashSet();
hashSet.add(1);         
hashSet.add(2);         
hashSet.contains(1);    // 返回 true
hashSet.contains(3);    // 返回 false (未找到)
hashSet.add(2);          
hashSet.contains(2);    // 返回 true
hashSet.remove(2);          
hashSet.contains(2);    // 返回  false (已经被删除)


注意:

  • 所有的值都在 [1, 1000000]的范围内。
  • 操作的总数目在[1, 10000]范围内。
  • 不要使用内建的哈希集合库。
class MyHashSet {
private:
    vector<int>hashset;
public:
    /** Initialize your data structure here. */
    MyHashSet() {
        hashset.resize(1000001);
        for(int i = 0; i < 1000000; i++)
            hashset[i] = 0;
    }
    
    void add(int key) {
        hashset[key] = 1;
    }
    
    void remove(int key) {
        hashset[key] = 0;
    }
    
    /** Returns true if this set contains the specified element */
    bool contains(int key) {
        return hashset[key] ? true : false;
    }
};

/**
 * Your MyHashSet object will be instantiated and called as such:
 * MyHashSet* obj = new MyHashSet();
 * obj->add(key);
 * obj->remove(key);
 * bool param_3 = obj->contains(key);
 */

设计哈希映射

不使用任何内建的哈希表库设计一个哈希映射

具体地说,你的设计应该包含以下的功能

  • put(key, value):向哈希映射中插入(键,值)的数值对。如果键对应的值已经存在,更新这个值。
  • get(key):返回给定的键所对应的值,如果映射中不包含这个键,返回-1。
  • remove(key):如果映射中存在这个键,删除这个数值对。


示例:

MyHashMap hashMap = new MyHashMap();
hashMap.put(1, 1);          
hashMap.put(2, 2);         
hashMap.get(1);            // 返回 1
hashMap.get(3);            // 返回 -1 (未找到)
hashMap.put(2, 1);         // 更新已有的值
hashMap.get(2);            // 返回 1 
hashMap.remove(2);         // 删除键为2的数据
hashMap.get(2);            // 返回 -1 (未找到) 
class MyHashMap {
private:
    vector<int>hashmap;
public:
    /** Initialize your data structure here. */
    MyHashMap() {
        hashmap.resize(1000001);
        for(int i = 1; i < 1000000;i++)
            hashmap[i] = -1;
    }
    
    /** value will always be non-negative. */
    void put(int key, int value) {
        hashmap[key] = value;
    }
    
    /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
    int get(int key) {
        return hashmap[key];
    }
    
    /** Removes the mapping of the specified value key if this map contains a mapping for the key */
    void remove(int key) {
        hashmap[key] = -1;
    }
};

/**
 * Your MyHashMap object will be instantiated and called as such:
 * MyHashMap* obj = new MyHashMap();
 * obj->put(key,value);
 * int param_2 = obj->get(key);
 * obj->remove(key);
 */

哈希集的用法

#include <unordered_set>                // 0. include the library

int main() {
    // 1. initialize a hash set
    unordered_set<int> hashset;   
    // 2. insert a new key
    hashset.insert(3);
    hashset.insert(2);
    hashset.insert(1);
    // 3. delete a key
    hashset.erase(2);
    // 4. check if the key is in the hash set
    if (hashset.count(2) <= 0) {
        cout << "Key 2 is not in the hash set." << endl;
    }
    // 5. get the size of the hash set
    cout << "The size of hash set is: " << hashset.size() << endl; 
    // 6. iterate the hash set
    for (auto it = hashset.begin(); it != hashset.end(); ++it) {
        cout << (*it) << " ";
    }
    cout << "are in the hash set." << endl;
    // 7. clear the hash set
    hashset.clear();
    // 8. check if the hash set is empty
    if (hashset.empty()) {
        cout << "hash set is empty now!" << endl;
    }
}

两个数组的交集

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

示例 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> num;
        unordered_set<int> ret;
        vector<int> result;
        for(int i = 0; i < nums1.size(); i++)
            num.insert(nums1[i]);
        for(int i = 0; i < nums2.size();i++)
        {
            if(num.find(nums2[i]) != num.end())
                ret.insert(nums2[i]);
        }
        for(auto it=ret.begin(); it != ret.end();it++)
            result.push_back(*it);
        return result;
    }
};

快乐数

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

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

示例: 

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

思路:我们可以用 HashSet 来记录所有出现过的数字,然后每出现一个新数字,在 HashSet 中查找看是否存在,若不存在则加入表中,若存在则跳出循环,并且判断此数是否为1,若为1返回true,不为1返回false。

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

哈希映射的用法

#include <unordered_map>                // 0. include the library

int main() {
    // 1. initialize a hash map
    unordered_map<int, int> hashmap;
    // 2. insert a new (key, value) pair
    hashmap.insert(make_pair(0, 0));
    hashmap.insert(make_pair(2, 3));
    // 3. insert a new (key, value) pair or update the value of existed key
    hashmap[1] = 1;
    hashmap[1] = 2;
    // 4. get the value of a specific key
    cout << "The value of key 1 is: " << hashmap[1] << endl;
    // 5. delete a key
    hashmap.erase(2);
    // 6. check if a key is in the hash map
    if (hashmap.count(2) <= 0) {
        cout << "Key 2 is not in the hash map." << endl;
    }
    // 7. get the size of the hash map
    cout << "the size of hash map is: " << hashmap.size() << endl; 
    // 8. iterate the hash map
    for (auto it = hashmap.begin(); it != hashmap.end(); ++it) {
        cout << "(" << it->first << "," << it->second << ") ";
    }
    cout << "are in the hash map." << endl;
    // 9. clear the hash map
    hashmap.clear();
    // 10. check if the hash map is empty
    if (hashmap.empty()) {
        cout << "hash map is empty now!" << endl;
    }
}

两数之和

给定一个整数数组 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> m;
        vector<int> ret;
        for(int i = 0; i < nums.size();i++)
            m.insert(make_pair(nums[i], i));
        for(int i = 0; i < nums.size(); i++)
        {
            if(m.count(target-nums[i]) && i != m[target-nums[i]])
            {
                ret.push_back(i);
                ret.push_back(m[target-nums[i]]);
                return ret;
            }
        }
        return ret;
    }
};

同构字符串

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

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

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

示例 1:

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

示例 2:

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

示例 3:

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

说明:
你可以假设 t 具有相同的长度。

思路:根据一对一映射的特点,我们需要用两个哈希表分别来记录原字符串和目标字符串中字符出现情况,由于ASCII码只有256个字符,所以我们可以用一个256大小的数组来代替哈希表,并初始化为0,我们遍历原字符串,分别从源字符串和目标字符串取出一个字符,然后分别在两个哈希表中查找其值,若不相等,则返回false,若相等,将其值更新为i + 1,因为默认的值是0,所以我们更新值为i + 1,这样当 i=0 时,则映射为1,如果不加1的话,那么就无法区分是否更新了

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        int n = s.length();
        int m = t.length();
        if(m != n)
            return false;
        int m1[256] = {0}, m2[256] = {0};
        int i = 0;
        while(i < n)
        {
            if(m1[s[i]] != m2[t[i]])
                return false;
            m1[s[i]] = i + 1;
            m2[t[i]] = i + 1;
            i++;
        }
        return true;
    }
};

 两个列表的最小索引总和

假设Andy和Doris想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示。

你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如果答案不止一个,则输出所有答案并且不考虑顺序。 你可以假设总是存在一个答案。

示例 1:

输入:
["Shogun", "Tapioca Express", "Burger King", "KFC"]
["Piatti", "The Grill at Torrey Pines", "Hungry Hunter Steakhouse", "Shogun"]
输出: ["Shogun"]
解释: 他们唯一共同喜爱的餐厅是“Shogun”。

示例 2:

输入:
["Shogun", "Tapioca Express", "Burger King", "KFC"]
["KFC", "Shogun", "Burger King"]
输出: ["Shogun"]
解释: 他们共同喜爱且具有最小索引和的餐厅是“Shogun”,它有最小的索引和1(0+1)。

提示:

  1. 两个列表的长度范围都在 [1, 1000]内。
  2. 两个列表中的字符串的长度将在[1,30]的范围内。
  3. 下标从0开始,到列表的长度减1。
  4. 两个列表都没有重复的元素。
class Solution {
public:
    vector<string> findRestaurant(vector<string>& list1, vector<string>& list2) {
        unordered_map<string, int> m1;
        unordered_map<string, int> m2;
        for(int i = 0; i < list1.size(); i++)
            m1[list1[i]] = i;
        for(int i = 0; i < list2.size(); i++)
            m2[list2[i]] = i;
        vector<int> index;
        for(auto it = m1.begin(); it != m1.end(); it++)
        {
            if(m2.find(it->first) != m2.end())
            {
                if(index.empty())
                {
                    index.push_back(it->second);
                    index.push_back(m2[it->first]);
                }
                else
                {
                    if(it->second + m2[it->first] < index[index.size()-1] + index[index.size()-2])
                    {
                        index.clear();
                        index.push_back(it->second);
                        index.push_back(m2[it->first]);
                    }
                    else if(it->second + m2[it->first] == index[index.size()-1] + index[index.size()-2])
                    {
                        index.push_back(it->second);
                        index.push_back(m2[it->first]);
                    }
                }
            }
        }
        vector<string> ret;
        if(index.empty())
            return ret;
        for(int i = 0; i < index.size();i = i + 2)
            ret.push_back(list1[index[i]]);
        return ret;
    }
};

两个数组的交集 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]
class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        int n1 = nums1.size(), n2 = nums2.size();
        vector<int> ret;
        if(n1 == 0 || n2 == 0)
            return ret;
        unordered_map<int, int> nums1_count;
        for(int i = 0; i < n1;i++)
        {
            if(nums1_count.find(nums1[i]) == nums1_count.end())
                nums1_count.insert(make_pair(nums1[i], 1));
            else
                nums1_count[nums1[i]]++;
        }
        for(int i = 0; i < n2; i++)
        {
            if(nums1_count[nums2[i]] > 0)
            {
                ret.push_back(nums2[i]);
                nums1_count[nums2[i]]--;
            }
        }
        return ret;
    }
};

存在重复元素 II

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 ij 的差的绝对值最大为 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
class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int, int> map;
        for(int i = 0; i < nums.size(); i++)
        {
            if(map.find(nums[i]) == map.end())
                map[nums[i]] = i;
            else
            {
                if(i - map[nums[i]] <= k)
                    return true;
                else
                    map[nums[i]] = i;
            }
        }
        return false;
    }
};

 字母异位词分组

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

示例:

输入: ["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>>ret;
        if(strs.empty())
            return ret;
        unordered_map<string, vector<string>>mstrs;
        for(auto s : strs)
        {
            string temp = s;
            sort(temp.begin(), temp.end());
            mstrs[temp].push_back(s);
        }
        for(auto m : mstrs)
            ret.push_back(m.second);
        return ret;
    }
};

36.有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

输入:
[
  ["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: true

示例 2:

输入:
[
  ["8","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
     但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。

说明:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 给定数独序列只包含数字 1-9 和字符 '.' 。
  • 给定数独永远是 9x9 形式的。
class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        for(int i = 0; i < 9; i++)
        {
            unordered_set<char> row;
            unordered_set<char> col;
            for(int j = 0; j < 9; j++)
            {
                if(board[i][j] != '.')
                {
                    if(row.find(board[i][j]) != row.end())
                        return false;
                    row.insert(board[i][j]);
                }
                
                if(board[j][i] != '.')
                {
                    if(col.find(board[j][i]) != col.end())
                        return false;
                    col.insert(board[j][i]);
                }
            }
        }
        for(int i = 0; i < 9; i = i + 3)
            for(int j = 0; j < 9; j = j + 3)
            {
                unordered_set<char> block;
                for(int x = i; x < i + 3;x++)
                    for( int y = j; y < j + 3; y++)
                    {
                        if(board[x][y] != '.')
                        {
                            if(block.find(board[x][y]) != block.end())
                                return false;
                             block.insert(board[x][y]);
                        }
                    }
            }
        return true;
    }
};

652.寻找重复的子树

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。

两棵树重复是指它们具有相同的结构以及相同的结点值。

示例 1:

        1
       / \
      2   3
     /   / \
    4   2   4
       /
      4

下面是两个重复的子树:

      2
     /
    4

    4

因此,你需要以列表的形式返回上述重复子树的根结点。

思路:将子树序列化,采用的是string来存储每个子树结构的hash映射,如果同一个字符串子树的节点不止一个,则曲第一个子树节点。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
        unordered_map<string, vector<TreeNode*> >map;
        vector<TreeNode *>ret;
        if(root == NULL)
            return ret;
        dfs(root, map);
        for(auto it = map.begin(); it != map.end(); it++)
        {
            if(map[it->first].size() > 1)
                ret.push_back(it->second[0]);
        }
        return ret;
    }
    string dfs(TreeNode *root, unordered_map<string, vector<TreeNode*>> &map)
    {
        if(root == NULL)
            return "#";
        string tmp = to_string(root->val) + dfs(root->left, map) + dfs(root->right, map);
        map[tmp].push_back(root);
        return tmp;
    }
};

 宝石与石头

 给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。

J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a""A"是不同类型的石头。

示例 1:

输入: J = "aA", S = "aAAbbbb"
输出: 3

示例 2:

输入: J = "z", S = "ZZ"
输出: 0

注意:

  • S 和 J 最多含有50个字母。
  •  J 中的字符不重复。
class Solution {
public:
    int numJewelsInStones(string J, string S) {
        unordered_map<char, int> mS;
        for(int i = 0; i < S.size(); i++)
        {
            if(mS.find(S[i]) == mS.end())
                mS[S[i]] = 1;
            else
                mS[S[i]]++;
        }
        int n = 0;
        for(int i = 0; i < J.size(); i++)
        {
            if(mS.find(J[i]) != mS.end())
                n += mS[J[i]];
        }
        return n;
    }
};

无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

思路:用集合记录字符,如果出现重复字符,则从左边开始一直删除直到重复元素没有了。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char> map;
        int left = 0, count = 0;
        int i = 0;
        while(i < s.length())
        {
            if(map.find(s[i]) == map.end())
            {
                map.insert(s[i]);
                i++;
            }
            else
            {
                count = max(count, (int)map.size());
                while(map.find(s[i]) != map.end())
                    map.erase(s[left++]);
            }
        }
        count = max(count, (int)map.size());        
        return count;
    }
};

四数相加 II

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

例如:

输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2

解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        unordered_map<int, int>map1;
        unordered_map<int, int>map2;
        int count = 0;
        for(int i = 0; i < A.size(); i++)
            for(int j = 0; j < B.size(); j++)
            {
                int res = A[i] + B[j];
                map1[res] ++;
            }
        for(int i = 0; i < C.size(); i++)
            for(int j = 0; j < D.size(); j++)
            {
                int res = C[i] + D[j];
                map2[res] ++;
            }
        for(auto it = map1.begin(); it != map1.end(); it++)
        {
            if(map2.find(-it->first) != map2.end())
                count += it->second * map2[-it->first];
        }
        return count;
    }
};

前K个高频元素

给定一个非空的整数数组,返回其中出现频率前 高的元素。

示例 1:

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

示例 2:

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

说明:

  • 你可以假设给定的 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 O(n log n) , 是数组的大小。

第一种方法:先用map记录每个数字出现的大小,然后使用最大堆来取出前K个元素。在C++中使用priority_queue来实现,默认是最大堆

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> m;
        priority_queue<pair<int, int>> q;
        vector<int> res;
        for (auto a : nums)
            ++m[a];
        for (auto it : m) 
            q.push({it.second, it.first});
        for (int i = 0; i < k; ++i) 
        {
            res.push_back(q.top().second); 
            q.pop();
        }
        return res;
    }
};

第二种方法:使用桶排序,在建立好数字和其出现次数的映射后,我们按照其出现次数将数字放到对应的位置中去,这样我们从桶的后面向前面遍历,最先得到的就是出现次数最多的数字,我们找到k个后返回即可。

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> map;
        int n = nums.size();
        for(int i = 0; i < n; i++)
        {
            if(map.find(nums[i]) == map.end())
                map[nums[i]] = 1;
            else
                map[nums[i]]++;
        }
        vector<vector<int>>count(n+1);
        for(auto it = map.begin(); it != map.end(); it++)
            count[it->second].push_back(it->first);
        int i = 0;
        vector<int> ret;
        for(int i = n; i >=0; i--)
        {
            for(int j = 0; j < count[i].size(); j++)
            {
                ret.push_back(count[i][j]);
                if(ret.size() == k)
                    return ret;
            }
        }
        return ret;
    }
};

常数时间插入、删除和获取随机元素

设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。

  1. insert(val):当元素 val 不存在时,向集合中插入该项。
  2. remove(val):元素 val 存在时,从集合中移除该项。
  3. getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。

示例 :

// 初始化一个空的集合。
RandomizedSet randomSet = new RandomizedSet();

// 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomSet.insert(1);

// 返回 false ,表示集合中不存在 2 。
randomSet.remove(2);

// 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomSet.insert(2);

// getRandom 应随机返回 1 或 2 。
randomSet.getRandom();

// 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomSet.remove(1);

// 2 已在集合中,所以返回 false 。
randomSet.insert(2);

// 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
randomSet.getRandom();

思路:使用额外的辅助结构,这里选的是在哈希表的基础上多增加一个数组,数组操作的时间复杂度和哈希表操作的时间复杂度均为O(1),哈希映射保存val->index的映射,vector保存index->val的映射。删除时,要保持index连续,因此用最后一个元素替代删除的元素,这样才能随机地返回一个元素。

class RandomizedSet {
private:
    vector<int>nums;
    unordered_map<int, int>map;
public:
    /** Initialize your data structure here. */
    RandomizedSet() {
        
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    bool insert(int val) {
        if(map.count(val))
            return false;
        nums.push_back(val);
        map[val] = nums.size()-1;
        return true;
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    bool remove(int val) {
        if(!map.count(val))
            return false;
        int last = nums.back();
        map[last] = map[val];
        nums[map[val]] = last;
        nums.pop_back();
        map.erase(val);
        return true;
    }
    
    /** Get a random element from the set. */
    int getRandom() {
        return nums[rand() % nums.size()];
    }
};

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet* obj = new RandomizedSet();
 * bool param_1 = obj->insert(val);
 * bool param_2 = obj->remove(val);
 * int param_3 = obj->getRandom();
 */

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值