map和set

map和set是两个主要的关联容器,但是底层都是红黑树实现
map
STL中关于map的理解:
map特性是,所有元素都会根据元素的键值自动排序,map的所有元素都是pair(结构体),同时有键值key和实值value。pair的第一个元素被视为键值,第二个元素视为实值,map不允许两个元素拥有相同的键值;
pair的定义:

template<class K,class V>
struct Pair
{
    K _first;
    V _second;

    Pair(const K&key, const V&value)
        :_first(key)
        , _second(value)
    {}
};

一:实现一个简单的字典插入,删除,查找

void TestDic()
{
map<string, string> dict;
//插入
dict.insert(make_pair("insert", "插入"));
dict.insert(make_pair("find", "查找"));
dict.insert(make_pair("erase", "删除"));
PrintfMap(dict);
//查找
map<string, string> ::iterator it = dict.find("find");
if(it ==dict.end())
{
    cout << "no found" << endl;
}
else
{
    cout << "found" << endl;
}
//删除
map<string, string> ::iterator it1 = dict.find("erase");
if (it1 == dict.end())
{
    cout << "no found" << endl;
}
else
{
    dict.erase(it1);
}
PrintfMap(dict);
}

二:map统计计数(TopK问题)
如:最喜欢吃的水果:

void TestTopk()
{
    //方法一:
    map<string, int> CountMap;
    string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
    for (size_t i = 0; i < sizeof(fruits)/sizeof(fruits[0]); ++i)
    {
        map<string, int>::iterator ret = CountMap.find(fruits[i]);
        if (ret != CountMap.end())
        {
            ret->second++;
        }
        else//第一次出现
        {
            CountMap.insert(make_pair(fruits[i], 1));
        }
    }
}

这里写图片描述
方法一的特点效率低,如果不存在,查找了两次;
方法二:

void TestTopk()
{
    //调pair<iterator, bool> insert(const value_type& val);
    /*
    insert 插入成功return pair,return false表明已经有节点存在
    */

    /*map<string, int> CountMap;
    string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
    for (size_t i = 0; i < sizeof(fruits) / sizeof(fruits[0]); ++i)
    {
         pair<map<string, int> ::iterator, bool> pr = CountMap.insert(make_pair(fruits[i], 1));
        if (pr.second == false) //已经存在++计数
        {
            pr.first->second++;
        }
    }
}

方法二的比方法一的效率高,只查找了找了一次;
方法三:

void TestTopk()
{
   map<string, int> CountMap;
    string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
    for (size_t i = 0; i < sizeof(fruits) / sizeof(fruits[0]); ++i)
    {
        CountMap[fruits[i]]++;
    }
}

如果前年的两种比较容易理解,那么这种方法会比较那理解,它是如何实现统计计数呢?
调用operator[]来实现

V&operator[](const k&key)
{
 return (*((this->insert(make_pair(k,mapped_typed()))).first)).second;
}

我们一层一层来分析:
1:mapped_typed()相当于调用V的缺省构造函数;
2:make_pair是pair的构造函数;里面有两个参数first和second;
3: first表示是迭代器,second表示是value;
4: 插入是insert返回值是pair;*解引用;取里面的数据;
5:如果没有则进行插入,如果存在把&value的值返回;
operator[]应用插入和修改

void PrintfMap(const map<string, string>&dict)
{
    map<string, string>::const_iterator it = dict.begin();
    while (it!=dict.end())
    {
        cout << (*it).first << ":" << it->second << endl;
        ++it;
    }
    cout << endl;
}
//operator[]的作用是插入和修改实现字典:
void TestMapDict()
{
    map<string, string> dict;
    dict["sort"] = "排序";
    dict["insert"] = "插入";
    dict["left"] = "左边";

    dict["sort"] = "***";
    PrintfMap(dict);
}

这里写图片描述
前面我们了解到Topk的几种算法,但我们希望统计最喜欢吃的水果的次数,按照排序跟家直观;那么怎样能够快速排序呢?
三:排序的几种常见算法:
在前面我们知道
堆排序,优先级队列;http://blog.youkuaiyun.com/f2016913/article/details/68483000
这里我们主要看map和set的实现:
1:map实现排序

void TestTopk()
{
    map<string, int> Countmap;
    string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
    for (size_t i = 0; i < sizeof(fruits) / sizeof(fruits[0]); ++i)
    {
        Countmap[fruits[i]]++;
    }

    vector<map<string, int>::iterator> v;
    map<string, int>::iterator CountIt = Countmap.begin();
    while (CountIt!=Countmap.end())
    {
        v.push_back(CountIt);
        ++CountIt;
    }
    //仿函数
    struct Compare
    {
        bool operator()(map<string, int>::iterator l, map<string, int>::iterator r)
        {
            return l->second > r->second;
        }
    };
    sort(v.begin(), v.end(), Compare());
}

这里写图片描述
2:Multimap实现:

void TestMultimap()
{
typedef multimap<string, string> Dict;
    typedef multimap<string, string>::iterator DictIt;

    Dict dict;
    dict.insert(make_pair("sort", "排序"));
    dict.insert(make_pair("sort", "排序"));
    dict.insert(make_pair("sort", "排序"));
    dict.insert(make_pair("left", "左边"));
    dict.insert(make_pair("left", "剩余"));

    DictIt  it = dict.begin();
    while (it!=dict.end())
    {
        cout << it->second << " ";
        ++it;
    }
    cout << endl;
}

这里写图片描述
multimap特性以及用法与map完全相同,唯一的差别在于:
1. 允许重复键值的元素插入容器(使用了RB-Tree的insert_equal函数)
2. 键值key与元素value的映照关系是多对多的关系
3. 没有定义[]操作运算
3:set实现:
set的特性是,所有元素会根据元素的键值自动排序,set元素不想map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,set不允许两个相同的键值;

void Testset()
{
    set<int> s;
    s.insert(1);
    s.insert(1);
    s.insert(1);
    s.insert(1);
    s.insert(1);
    s.insert(4);
    s.insert(2);
    s.insert(3);
    s.insert(5);

    set<int> ::iterator it = s.begin();
    while (it!=s.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
}

这里写图片描述
set不仅会排序,而且会去重
4:mulitiset的实现:
mulitise的特性以及用法和set完全相同,唯一的差别是允许键值的重复

void Testmultiset()
{
multiset<int> s;
    s.insert(1);
    s.insert(1);
    s.insert(1);
    s.insert(1);
    s.insert(1);
    s.insert(4);
    s.insert(2);
    s.insert(3);
    s.insert(5);

    set<int> ::iterator it = s.begin();
    while (it!=s.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
}

这里写图片描述

### ES6中MapSet的区别与使用方法 #### 1. 数据结构与存储方式 Map 是一种键值对的集合,任何类型的值(包括对象)都可以作为键或值。与对象不同的是,Map 的键是有序的,所以当你遍历它的时候,会按照插入顺序来[^3]。 而 Set 是一种集合数据结构,其中的元素都是唯一的,不允许重复。Set 可以通过数组或其他可迭代对象创建,并且支持添加、删除、查找等操作[^4]。 #### 2. 创建方式 - **Map**:可以通过空构造函数创建一个空的 Map,或者通过传入一个二维数组来初始化。 ```javascript let myMap = new Map(); // 空 Map let myMapWithValues = new Map([['key1', 'value1'], ['key2', 'value2']]); ``` - **Set**:可以通过空构造函数创建一个空的 Set,或者通过传入一个数组来初始化。 ```javascript let mySet = new Set(); // 空 Set let mySetWithValues = new Set([1, 2, 3]); ``` #### 3. 添加元素 - **Map**:使用 `set` 方法添加键值对。 ```javascript myMap.set('name', '张三'); myMap.set(1, 'one'); ``` - **Set**:使用 `add` 方法添加元素。 ```javascript mySet.add(1); mySet.add('string'); ``` #### 4. 删除元素 - **Map**:使用 `delete` 方法删除指定键的键值对。 ```javascript myMap.delete('name'); // 删除键为 'name' 的键值对 ``` - **Set**:使用 `delete` 方法删除指定元素。 ```javascript mySet.delete(1); // 删除值为 1 的元素 ``` #### 5. 清空所有数据 - **Map**:使用 `clear` 方法清空所有键值对。 ```javascript myMap.clear(); // 移除所有键值对 ``` - **Set**:使用 `clear` 方法清空所有元素。 ```javascript mySet.clear(); // 移除所有元素 ``` #### 6. 获取大小 - **Map**:使用 `size` 属性获取键值对的数量。 ```javascript console.log(myMap.size); // 输出:2 ``` - **Set**:使用 `size` 属性获取元素的数量。 ```javascript console.log(mySet.size); // 输出:3 ``` #### 7. 检查是否存在 - **Map**:使用 `has` 方法检查某个键是否存在。 ```javascript console.log(myMap.has('name')); // 输出:false ``` - **Set**:使用 `has` 方法检查某个元素是否存在。 ```javascript console.log(mySet.has(1)); // 输出:true ``` #### 8. 遍历 - **Map**:可以通过多种方式进行遍历,例如 `for...of` 循环或 `forEach` 方法。 ```javascript for (let [key, value] of myMap) { console.log(key, value); } myMap.forEach((value, key) => { console.log(key, value); }); ``` - **Set**:同样可以通过 `for...of` 循环或 `forEach` 方法进行遍历。 ```javascript for (let value of mySet) { console.log(value); } mySet.forEach(value => { console.log(value); }); ``` ### 总结 Map Set 在 ES6 中都是非常有用的数据结构,但它们的用途有所不同。Map 更适合存储键值对,尤其是当键需要是复杂类型时;而 Set 则更适合存储唯一值的集合[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值