unordered系列容器OJ

本文介绍了C++标准库中的unordered系列容器,如unordered_map、unordered_set等,强调了它们基于哈希表的特性,如快速访问、无序性和自定义哈希函数。文章详细讲解了如何用这些容器解决重复元素查找、两个数组交集等问题。
摘要由CSDN通过智能技术生成

目录

1、unordered系列容器

2、unordered系列容器OJ

1、重复n次的元素

2、两个数组的交集I

3、两个数组的交集II

4、存在重复元素

5、两句话中不常见的单词


1、unordered系列容器

在C++标准库中,unordered系列容器是基于哈希表实现的,

用于存储唯一键(对于unordered_set)或键值对(对于unordered_map),

其中键具有唯一性。与mapset这类基于红黑树的容器相比,哈希表能够提供平均时间复杂度为O(1)的快速访问性能,不过在最坏情况下可能退化到O(n)。

主要的unordered系列容器包括:

unordered_map: 一个关联容器,包含键值对,键是唯一的。通过键可以快速访问对应的值。

unordered_multimap: 一个关联容器,包含键值对,但键可以重复。即一个键可以对应多个值。

unordered_set: 一个集合容器,包含唯一的元素。

unordered_multiset: 一个集合容器,元素可以重复

主要特点:

无序性:这些容器中的元素不会按任何特定顺序存储,因而不能像有序容器那样进行范围查询。

快速访问:提供了非常快速的访问能力,平均情况下时间复杂度为O(1)。

自定义哈希函数:用户可以提供自定义的哈希函数来改善元素的分布,从而提升性能。

键的唯一性(对于unordered_setunordered_map:保证了每个键的唯一性。

键的非唯一性(对于unordered_multisetunordered_multimap:允许键重复,实现一键多值的存储。

unordered系列容器适用于以下场景:

当需要快速查找、插入和删除元素时。

当元素的顺序不重要时。

当不需要对元素进行范围查询时。

需要注意的是:

1、因为容器内部是无序的,所以遍历这些容器得到的元素顺序是不确定的。

2、当哈希表的负载因子(元素数量/桶数量)过高时,性能可能会下降。因此,适时地调整哈希表的大小是很重要的。

3、在使用自定义类型作为键时,需要确保提供有效的哈希函数和相等判断函数。

2、unordered系列容器OJ

1、重复n次的元素

给你一个整数数组 nums ,该数组具有以下属性:

  • nums.length == 2 * n.
  • nums 包含 n + 1 个 不同的 元素
  • nums 中恰有一个元素重复 n 次

找出并返回重复了 n 次的那个元素。

思路:

元素出现了n次,所以元素每出现一次就要记录一次次数,而unordered_map就很适合,可以用唯一的key,也就是这个元素对应的值不断++,将所有元素存储到map后,再遍历里面的元素,直到找到对应value等于n次的key值,就是需要求的值。

代码实现:

class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        int n=nums.size();
        int k=n/2;
        unordered_map<int,int> m;
        for(auto &e:nums)
        {
            m[e]++;
        }
        for(auto &e:m)
        {
            if(e.second==k)
            {
                return e.first;
            }
        }
        return -1;
    }
};

2、两个数组的交集I

给定两个数组 nums1 和 nums2 ,返回 它们的 交集输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

示例 1:

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

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

思路:

分别将nums1和nums2的元素插入s1,s2两个unordered_set容器去重,再遍历s1容器,同时在s2中查找这个遍历的元素,若存在则将其存入vector数组中返回。

代码实现:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ret;
        unordered_set<int> s1;
        for(int &e:nums1)
        {
            s1.insert(e);
        }
        unordered_set<int> s2;
        for(int &e:nums2)
        {
            s2.insert(e);
        }
        
        for(const int &e: s1)
        {
            if(s2.find(e)!=s2.end())
            {
                ret.push_back(e);
            }
        }
        return ret;
    }
};

3、两个数组的交集II

给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

示例 1:

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

示例 2:

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

思路:

整体思路与上题类似,不过要将交集元素返回在两数组中出现的最小次,所以我们使用unorder_map来存储元素以及出现次数,遍历第一个map,将交集元素往返回数组中存

两个pair中最小的second次。

实现代码:

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ret;
        unordered_map<int,int> m1;
        for(int &e:nums1)
        {
            m1[e]++;
        }
        unordered_map<int,int> m2;
        for(int &e:nums2)
        {
            m2[e]++;
        }
        for(auto &e:m1)
        {
            auto tmp=m2.find(e.first);
            if(tmp!=m2.end())
            {
                int n=min(e.second,tmp->second);
                while(n--)
                {
                    ret.push_back(e.first);
                }
            }
        }
        return ret;

    }
};

4、存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

示例 1:

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

示例 2:

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

示例 3:

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

思路:

因为unordered_set: 一个集合容器,包含唯一的元素。

所以我们利用insert的返回值,每插入一次使用一个变量来记录返回值,判断是否插入成功就可以直到是否存在重复元素:

所有元素都插入成功则没有重复元素

一个插入失败则存在重复元素。

实现代码:

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> s1;
        for(int &e:nums)
        {
            auto tmp=s1.insert(e);
            if(!tmp.second)
            return true;
        }
        return false;
    }
};

5、两句话中不常见的单词

句子 是一串由空格分隔的单词。每个 单词 仅由小写字母组成。如果某个单词在其中一个句子中恰好出现一次,在另一个句子中却 没有出现 ,那么这个单词就是 不常见的 。给你两个 句子 s1 和 s2 ,返回所有 不常用单词 的列表。返回列表中单词可以按 任意顺序 组织。

示例 1:

输入:s1 = "this apple is sweet", s2 = "this apple is sour"
输出:["sweet","sour"]

示例 2:

输入:s1 = "apple apple", s2 = "banana"
输出:["banana"]

思路:

需要找到在两个句子中出现一次的单词,需要将两个句子中的所有单词存到unordered_map<string,int>中,然后遍历map,将second等于1的单词都存到返回数组中。

就可以得到符合要求的值。

但是需要注意的是,首先需要使用 splitWords 函数将句子分解为单词。

实现代码:

class Solution {
public:
    vector<string> splitWords(string s) {
    vector<string> words;
    string word;
    int start = 0;

    // 循环遍历字符串s
    for (int i = 0; i < s.length(); ++i) {
        // 当遇到空格时,将[start, i)范围内的子字符串作为一个单词添加到单词列表中
        if (s[i] == ' ') {
            word = s.substr(start, i - start);
            words.push_back(word);
            start = i + 1; // 更新下一个单词的起始位置
        }
    }
    // 处理最后一个单词
    word = s.substr(start);
    if (!word.empty()) {
        words.push_back(word);
    }

    return words;
}
    vector<string> uncommonFromSentences(string s1, string s2) {
        vector<string> ret,ss1,ss2;
        ss1=splitWords(s1);
        ss2=splitWords(s2);
        unordered_map<string,int> m;
        for(string&e:ss1)
        {
            m[e]++;
        }
        for(string&e:ss2)
        {
            m[e]++;
        }
        for(auto e:m)
        {
            if(e.second==1)
            {
                ret.push_back(e.first);
            }
        }
        return ret;

    }
};

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值