从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别(中)

简介: 从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别

从C语言C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别(上):https://developer.aliyun.com/article/1521953

3.3 map的容量和操作函数

方括号是map的很特别的操作,其它不是连续空间存储的都没有,但是map的方括号和普通的也不一样,它返回的是键值对中key对应的value的引用。

当key不在map中时,通过operator[ ] 获取对应value时会发生什么问题?

在元素访问时,有一个与operator[ ]类似的操作:at()函数(该函数不常用),都是通过key找到与key对应的value然后返回其引用,

不同的是:当key不存在时,operator[ ]用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。

上面方括号调用的那句代码分成两句就是这样:


3.4 map使用代码

用map来创建字典:

void test_map1()
{
  map<string, string> dict;
  //pair<string, string> kv1("sort", "排序");
  //dict.insert(kv1);
 
  dict.insert(pair<string, string>("sort", "排序")); // 等价于上面注释
  dict.insert(pair<string, string>("test", "测试"));
  dict.insert(pair<string, string>("string", "字符串"));
  typedef pair<string, string> DictKV;
  dict.insert(DictKV("string", "xxx"));
  dict.insert(make_pair("left", "左边")); // 常用这种插入
  dict["right"] = "右边"; // 更常用这种插入
 
  //map<string, string>::iterator it = dict.begin();
  auto it = dict.begin(); // 等价于上面注释
  while (it != dict.end())
  {
    //cout << (*it).first << (*it).second <<endl;
    cout << it->first << ":" << it->second << endl; // 等价于上面注释
    ++it;
  }
  cout << endl;
 
  for (const auto& kv : dict)
  {
    cout << kv.first << ":" << kv.second << endl;
  }
  cout << endl;
}
 
int main()
{
  //test_set();
  //test_multiset();
  test_map1();
 
  return 0;
}


用map来计算次数:

void test_map2()
{
  string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
  //map<string, int> countMap;
  //for (const auto& str : arr)
  //{
  //  map<string, int>::iterator it = countMap.find(str);
  //  if (it != countMap.end())
  //  {
  //    //(*it).second++;
  //    it->second++;
  //  }
  //  else
  //  {
  //    countMap.insert(make_pair(str, 1));
  //  }
  //}
  map<string, int> countMap;
  for (const auto& str : arr) // 等同于上面一大段注释
  {
    // 1、str不在countMap中,插入pair(str, int()),然后在对返回次数++
    // 2、str在countMap中,返回value(次数)的引用,次数++;
    countMap[str]++;
  }
 
  map<string, int>::iterator it = countMap.begin();
  while (it != countMap.end())
  {
    cout << it->first << ":" << it->second << endl;
    ++it;
  }
  cout << endl;
}
 
int main()
{
  //test_set();
  //test_multiset();
  //test_map1();
  test_map2();
 
  return 0;
}


3.5 multimap使用

multimap和multiset一样是允许键值冗余的,


1. multimap是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对,其中多个键值对之间的key是可以重复的。

2. 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,value_type是组合key和value的键值对 :typedef pair value_type;

3. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key进行排序的。

4. multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历multimap中的元素可以得到关于key有序的序列。

5. multimap在底层用二叉搜索树(红黑树)来实现。

注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。


multimap中的接口可以参考map,功能都是类似的。

注意:

1. multimap中的key是可以重复的。

2. multimap中的元素默认将key按照小于来比较

3. multimap中没有重载operator[ ]操作。

4. 使用时与map包含的头文件相同。

void test_multimap()
{
  map<string, string> dict;
  dict.insert(make_pair("sort", "排序"));
  dict["insert"];
  dict["insert"] = "插入";
  dict["left"] = "左边";
  dict["left"] = "左边";
  for (const auto& kv : dict)
  {
    cout << kv.first << ":" << kv.second << endl;
  }
  cout << dict.size() << endl;
 
  multimap<string, string> mdict;
  mdict.insert(make_pair("sort", "排序"));
  mdict.insert(make_pair("right", "右边"));
  mdict.insert(make_pair("right", "正确"));
  mdict.insert(make_pair("right", "右边")); // 正常插入,不管key值
  for (const auto& kv : mdict)
  {
    cout << kv.first << ":" << kv.second << endl;
  }
  cout << mdict.size() << endl;
}
 
int main()
{
  //test_set();
  //test_multiset();
  //test_map1();
  //test_map2();
  test_multimap();
 
  return 0;
}


4. 笔试OJ题

set和map在很多统计次数的OJ中都能用,这里先写两道:

692. 前K个高频单词 - 力扣(LeetCode)

难度中等


给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。


返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。


示例 1:


输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2

输出: ["i", "love"]

解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。

   注意,按字母顺序 "i" 在 "love" 之前。

示例 2:


输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4

输出: ["the", "is", "sunny", "day"]

解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,

   出现次数依次为 4, 3, 2 和 1 次。

注意:


1 <= words.length <= 500

1 <= words[i] <= 10

words[i] 由小写英文字母组成。

k 的取值范围是 [1, 不同 words[i] 的数量]

进阶:尝试以 O(n log k) 时间复杂度和 O(n) 空间复杂度解决。

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
 
    }
};

priority_queue解析代码:

这道题是topk问题和统计次数的融合,就是先排次数(val)多的k个,次数一样多的按key排降序,

map里面是不管key的,这样我们就要写一个排序的仿函数了,先用优先级队列(堆)写一写:

优先级队列默认大的优先级高,传的是 less 仿函数,底层是一个大堆;

如果想控制小的优先级高,需手动传 greater 仿函数,其底层是一个小堆。

我们要弄一个大堆,大堆key一样的时候,按val弄一个小堆:

class Solution {
public:
    struct Less
    {
        bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2) const
        {
            if(kv1.second < kv2.second) // second(int)升序,小的在前面就ture
            {
                return true;
            }
            if(kv1.second == kv2.second && kv1.first > kv2.first) // first(string)降序
            {
                return true;
            }
            return false;
        }
    };
 
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> countMap;
        for(const auto& str : words) // 统计次数
        {
            countMap[str]++;
        }
 
        /*priority_queue<pair<string,int>,vector<pair<string,int>>,Less> MaxHeap;
        for(const auto& kv : countMap) // 也可以迭代器区间初始化,就是太长了,长就typedef
        {
            MaxHeap.push(kv);
        }*/
        typedef priority_queue<pair<string,int>,vector<pair<string,int>>,Less> MaxHeapType;
        MaxHeapType MaxHeap(countMap.begin(),countMap.end());
 
        vector<string> v;
        while(k--)
        {
            v.push_back(MaxHeap.top().first);
            MaxHeap.pop();
        }
        return v;
    }
};

sort解析代码:

在上面的基础上想想,如果我们用一个稳定的排序来排序string,是不是就能解决?

虽然sort不能排序map,但是可以把map转移到vector里然后在sort,直接sort是不稳定的,

但我们可以控制仿函数来间接控制:

(下面几种方法已经不是为了解题了,只是为了熟悉各个方法的使用,

因为priority的仿函数是反过来的,这里只需改下仿函数的大于小于号,把Less改成Great:)

class Solution {
public:
    struct Great
    {
        bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2) const
        {
            if(kv1.second > kv2.second)
            {
                return true;
            }
            if(kv1.second == kv2.second && kv1.first < kv2.first)
            {
                return true;
            }
            return false;
        }
    };
 
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> countMap;
        for(const auto& str : words) // 统计次数
        {
            countMap[str]++;
        }
 
        vector<pair<string,int>> sortV(countMap.begin(),countMap.end());
        sort(sortV.begin(),sortV.end(),Great());
 
        vector<string> v;
        for(int i = 0; i < k; ++i)
        {
            v.push_back(sortV[i].first);
        }
        return v;
    }
};

从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别(下):https://developer.aliyun.com/article/1521958

目录
相关文章
|
5月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
229 26
|
9月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
519 73
|
安全 编译器 C语言
C++入门1——从C语言到C++的过渡
C++入门1——从C语言到C++的过渡
260 2
|
6月前
|
存储 编译器 容器
set、map、multiset、multimap的介绍及使用以及区别,注意事项
set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列。set当中存储元素的value都是唯一的,不可以重复,因此可以使用set进行去重。set默认是升序的,但是其内部默认不是按照大于比较,而是按照小于比较。set中的元素不能被修改,因为set在底层是用二叉搜索树来实现的,若是对二叉搜索树当中某个结点的值进行了修改,那么这棵树将不再是二叉搜索树。
260 0
|
算法 编译器 C语言
【C语言】C++ 和 C 的优缺点是什么?
C 和 C++ 是两种强大的编程语言,各有其优缺点。C 语言以其高效性、底层控制和简洁性广泛应用于系统编程和嵌入式系统。C++ 在 C 语言的基础上引入了面向对象编程、模板编程和丰富的标准库,使其适合开发大型、复杂的软件系统。 在选择使用 C 还是 C++ 时,开发者需要根据项目的需求、语言的特性以及团队的技术栈来做出决策。无论是 C 语言还是 C++,了解其优缺点和适用场景能够帮助开发者在实际开发中做出更明智的选择,从而更好地应对挑战,实现项目目标。
468 0
|
C语言 C++
实现两个变量值的互换[C语言和C++的区别]
实现两个变量值的互换[C语言和C++的区别]
202 0
|
3月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
1060 0
|
11月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
722 23
|
5月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
341 15
|
10月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
452 1
一文彻底搞清楚C语言的函数