C++之关联容器

文章目录

  • 概述及类型
  • map
  • set
  • pair类型
    • 概念
    • 初始化
      • 默认初始化
      • 提供初始化器
    • 允许的操作
      • 可以创建一个pair类的函数
      • 可以作为容器的类型
  • 关联容器迭代器
    • 概念
    • map的迭代器
    • set的迭代器是const的
  • 初始化
    • map and set
    • multimap and multiset
  • 关联容器的操作
    • 额外的类型别名
    • 关联容器和算法
    • 删除元素
    • 添加元素
      • insert操作
        • 通过insert操作向map添加元素
        • insert的返回值
        • 向两种multi容器添加元素
    • map的下标操作
      • 下标操作的返回值
      • 通过下标([])对map的key与value进行操作:
    • 访问元素
      • 概念
      • 利用find和count进行查找
      • lower_bound和upper_bound面向迭代器
      • equal_range函数
    • 下标和insert的区别、下标与find的区别
  • 有序容器
    • 关键字类型
    • 重载关键字类型代替<运算符
  • 无序容器
    • 关键字类型
    • 管理桶
  • 关键字相同的元素相邻存储


概述及类型

关联容器和顺序容器有着根本的不同:

  • 关联容器中的元素是按关键字来保存和访问的。
  • 顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。
  • 顺序容器的底层数据结构是数组、链表。
  • 关联容器的底层数据结构是红黑树(set、multimap)、哈希表等。

关联容器支持高效的关键字查找和访问。两个主要的关联容器(associative-container)类型是map和set:

  • map中的元素是一些关键字-值(key-value)对。
  • set中每个元素只包含一个关键字;

标准库提供8个关联容器,这8个容器间的不同体现在三个维度上:每个容器

  1. 或者是一个set,或者是一个map;
  2. 或者要求不重复的关键字,或者允许重复关键字;
  3. 按顺序保存元素,或无序保存。

特性由名字表示:

  • 允许重复关键字的容器的名字中都包含单词multi;
  • 不保持关键字按顺序存储的容器的名字都以单词unordered开头。

因此一个unordered_multi_set是一个允许重复关键字,元素无序保存的集合,而一个set则是一个要求不重复关键字,有序存储的集合。无序容器使用哈希函数来组织元素。

所在的头文件:

  • 类型map和multimap定义在头文件map中;
  • set和multiset定义在头文件set中;
  • 无序容器则定义在头文件unordered_map和unordered_set中。

在这里插入图片描述
下表中的操作关联容器和顺序容器都支持:
在这里插入图片描述



map

为了高效实现 “按值访问元素” 这个操作而设计的。

当从map中提取一个元素时,会得到一个pair类型的对象。

简单来说,pair是一个模板类型,保存两个名为 firstsecond 的(公有)数据成员。



set

保存特定的值(如:满足/不满足,忽略/不忽略)集合。



pair类型

概念

  • pair是标准库类型,定义在头文件utility中。
  • pair的数据成员是public的。两个成员分别命名为 first 和 second。可以用成员访问符号(.)来访问它们。
  • map的元素是pair,pair用first成员保存关键字,用second成员保存对应的值。

初始化

默认初始化

一个pair保存两个数据成员。创建一个pair时,我们必须提供两个类型名,但是不要求一样:
在这里插入图片描述
pair的默认构造函数对数据成员进行值初始化。因此:

  • anon是两个空的string
  • line保存一个空的string一个空的vector
  • word_count保存一个空串和一个为0的size_t

提供初始化器

pair <string, string> anon = {"zhangsan", "nan"};

允许的操作

在这里插入图片描述


可以创建一个pair类的函数

可以利用c++11标准下,允许对返回值进行列表初始化的特性,完成这一目的:

pair<string, int>
fun(vector<string> &vs){if(!vs.empty())return {vs.back(), vs.back().size()}; // 列表初始化//返回尾元素和尾元素的大小组成的pairelsereturn pair<string,int>(); // 隐式构造返回值,返回一个空串和0构成的pair
}

也可以用make_pair来生成pair对象,pair的两个类型来自于make_pair的参数:

return make_pair(vs.back(), v.back().size());

可以作为容器的类型

在这里插入图片描述
push_back的参数也可以写成列表初始化的形式或者使用make_pair:

arr.push_back({s, i});
arr.push_back(make_pair(s, i));


关联容器迭代器

概念

当解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用。

迭代器的类型:

  • map:pair<关键字类型,值类型>::iterator
  • set:set<元素值类型>::iterator

对于自定义的比较类型的set或者multiset:

set<A, decltype(compareA)*> Aset(compareA);

其迭代器类型应为:pair<关键字类型,自定义比较类型>::iterator


map的迭代器

一个map的value_type是一个pair。可以改变pair的值(即改变映射关系),但不能改变关键字成员的值,因为它是const的。
在这里插入图片描述


set的迭代器是const的

我们在概念中说过:当解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用。但set的value_type即使它的key_type。因此,虽然set类型同时定义了iterator和const_iterator类型,但两种类型都只允许只读访问set中的元素。

与不能改变一个map元素的关键字一样,一个set中的关键字也是const的。可以用一个set迭代器来读取元素的值,但不能修改:
在这里插入图片描述



初始化

map and set

  • 每个关联容器都定义了一个默认构造函数,它创建一个指定类型的空容器。
  • 可以将关联容器初始化为另一个同类型容器的拷贝。
  • 或是从一个值范围来初始化关联容器,只要这些值可以转化为容器所需类型就可以。

在新标准下,我们也可以对关联容器进行值初始化
在这里插入图片描述
正如上图所示,我们初始化map时,必须将每个关键字 - 值对包围在花括号中:

(key,value)


multimap and multiset

一个map或set中的关键字必须是唯一的,即,对于一个给定的关键字,只能有一个元素的关键字等于它。容器multimap和multiset没有此限制,它们都允许多个元素具有相同的关键字。

实例:
在这里插入图片描述



关联容器的操作

额外的类型别名

在这里插入图片描述
对于set类型,key_type和value_type是一样的——set中保存的值就是关键字。

在一个map中,元素是关键字-值对。即,每个元素是一个pair对象,包含一个关键字和一个关联的值。由于我们不能改变一个元素的关键字,因此这些pair的关键字部分是const的:在这里插入图片描述


关联容器和算法

由上面的知识可知,set类型中的元素是const的,map中的元素是pair,其第一个成员是const的。不能通过迭代器更改set中的元素的值、以及map的关键字的值。也就意味着关联容器只可用于只读算法。

但是由于只读算法都要搜索序列。关联容器又不能通过关键字遍历查找, 因此关联容器定义了很多特有的搜索成员,通过给定的关键字直接获取元素。

因此如果我们真要对一个关联容器使用算法,要么将它当成一个源序列,要么将它当作一个目的位置。


删除元素

在这里插入图片描述
删除关键字的版本:对于保存不重复关键字的容器,erase的返回值总是0 or 1,0表示想删除的元素并不在容器中。


添加元素

insert操作

在这里插入图片描述
insert有两个版本,分别接受一对迭代器,或者是一个初始化器列表,这两个版本对于一个给定的关键字,只有第一个带此关键字的元素才被插入到容器中:
在这里插入图片描述


通过insert操作向map添加元素

对一个map进行insert操作时,必须记住元素类型是pair。

通常,对于想要插入的数据,并没有一个现成的pair对象。可以在insert的参数列表中创建一个pair:
在这里插入图片描述


insert的返回值

insert(或emplace)返回的值依赖于容器类型和参数。对于不包含重复关键字的容器,添加单一元素的insert和emplace版本返回一个pair,告诉我们插入操作是否成功。pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在于容器中。如果关键字已在容器中,则insert什么事情也不做,且返回值中的bool部分为false。如果关键字不存在,元素被插入容器中,且bool值为true。


向两种multi容器添加元素

与map和set不同,一个multi容器中的关键字不必唯一,在这些类型上调用insert总会插入一个元素。返回一个指向新元素的迭代器。


map的下标操作

在这里插入图片描述

  • map和unordered_map容器提供了下标运算符和一个对应的at函数。
  • set类型不支持下标,因为set中没有与关键字相关联的“值”。 元素本身就是关键字,因此“获取与一个关键字相关联的值”的操作就没有意义了。
  • 不能对一个multimap或一个unordered_multimap进行下标操作,因为这些容器中可能有多个值与一个关键字相关联。

类似我们用过的其他下标运算符,map下标运算符接受一个索引(即,一个关键字),获取与此关键字相关联的值。

但是,与其他下标运算符不同的是(对于其他下标运算符而言,访问一个不存在地址/索引显然是一个未定义的行为),如果关键字并不在map中,会为它创建一个元素并插入到map中,并对关联值进行值初始化。

用一个实例来看:
在这里插入图片描述
将会执行如下操作:

  • 在word_count中搜索关键字为Anna的元素,未找到。
  • 将一个新的关键字-值对插入到word_count中。关键字是一个const string,保存Anna。值进行值初始化,在本例中值为0。
  • 提取出新插入的元素(键值对),并将值1赋予它(它指的是键值对中的值)。

由于下标运算符可能插入一个新元素,因此只可以对非const的map使用下标操作。


下标操作的返回值

通常情况下,解引用一个迭代器所返回的类型与下标运算符返回的类型是一样的(如vector和string)。但对map则不然:

  • 当对一个map进行下标操作时,会获得一个mapped_type对象;
  • 当解引用一个map迭代器时,会得到一个value_type对象。

通过下标([])对map的key与value进行操作:

void add_val(map<string, vector<string>>& map, const string& key,const string& s){map[key].push_back(s);// 当map[key]对应的vector<string>存在时直接将s push_back进去// 不存在时首先创建一个空的vector<string>,再将s push_back进去
}void add_key(map<string, vector<string>>& map, const string& key) {map[key];// 当map[key]对应的vector<string>存在时只是获取其vector,但不做任何操作// 不存在时,在容器中为key创建一个对象,进行默认初始化,构造一个空的vector//等价于:/*if(map.find(key) == map.end()){map.[key] = vector<string>();}find函数找到参数返回参数对应的迭代器,未找到返回尾迭代器。*/
}
int main(int argc, char const *argv[]) {map<string, vector<string>> map;add_key(map, "li");add_key(map, "chen");add_key(map, "jiang");add_val(map, "li", "si");add_val(map, "chen", "chouchou");add_val(map, "li", "wu");add_val(map, "chen", "bianbian");for(auto& i : map){cout << i.first << ": ";for(auto& j : i.second){cout << j << " ";}cout << endl;}return 0;
}

其实map<string, vector<string>>multimap<string, string>起到的作用是类似的。

如果用multimap存储的话只是不需要add_key函数了、add_value函数可以直接用insert操作。


访问元素

概念

在这里插入图片描述
在这里插入图片描述


利用find和count进行查找

以multi容器为例,容器中如果有多个元素具有一样的关键字,则这些元素在容器中会相邻存储。

那么想要遍历一个关键字的所有元素应该这么做:

  auto countover = multi.count(key); // 关键词出现的次数auto findover = multi.find(key); // 具有给定关键字的第一个元素while (countover--) { // 循环次数为关键字出现的次数cout << findover->second << endl; // 关键字对应的值findover++; // 后移一位元素,相同的关键字相邻排列}

lower_bound和upper_bound面向迭代器

lower_bound和upper_bound返回一个范围:

  • 如果关键字在容器中,lower_bound返回的迭代器指向第一个具有关键字的元素;upper_bound返回的迭代器指向最后一个具有关键字的元素的后面位置。 也就是类似于begin和end迭代器组成的左闭右开区间,表示所有具有该关键字的元素的范围。
  • 如果关键字不在容器内, 由于lower_bound和upper_bound面向有序的关联容器,因此两者返回的迭代器相同,都指向第一个关键字大于k(给定关键字)的元素。

因此可以重写上面的程序:

for(auto beg = multi.lower_bound(key); beg != multi.upper_bound(key); beg++){cout << beg -> second << endl;
}

equal_range函数

用equal_range函数重写上面的程序:

for(auto pos = multi.equal_range(key); pos.first != pos.second; pos.first++){cout << (pos.first)->second << endl;
}

对照使用lower_bound和upper_bound的代码,可以清晰看出来pos.first等价于beg,pos.second等价于end。返回的都是指向一个multi元素的迭代器,对其解引用会得到一个multi的value_type对象——pair,因此需要通过->来访问second——也就是“键-值对”中的值。


下标和insert的区别、下标与find的区别

[]与insert不同,

  • insert: 当map的key存在时不做任何操作;不存在时插入键值对。
  • []: 当key存在时,用push_back将value添加进去;如果不存在,创建key然后用push_back将value添加进去。

两者在key不存在时操作是类似的。存在时,insert维护原有键值对(不对容器进行更改),下标使用最新的键值对覆盖原有的键值对(对容器进行更改)。

[]与find不同:

  • []: key不存在时,构造一个元素,将其插入到容器;存在时,获取其元素
  • find: key不存在时,返回尾后迭代器;存在时,返回的迭代器指向第一次出现的关键字的位置

两者在key存在时操作是类似的。不存在时,find不做更改容器的操作,下标会对容器的内容进行更改。



有序容器

关键字类型

有序容器使用比较函数来比较关键字,从而将元素按顺序存储。

对于有序容器——map、multimap、set以及multiset,关键字类型必须定义元素比较的方法。 默认情况下,标准库使用关键字类型的<运算符来比较两个关键字。在集合类型set中,关键字类型就是元素类型;在映射类型map中,关键字类型是元素的第一部分的类型。

c++11允许提供自己定义的操作来代替关键字上的<运算符。所提供的操作必须在关键字类型上定义一个严格弱序(strict weak ordering)。可以将严格弱序看作“小于等于”:

  • 两个关键字不能同时“小于等于”对方;如果k1“小于等于”k2,那么k2绝不能“小于等于”k1。
  • 如果k1“小于等于”k2,且k2“小于等于”k3,那么k1必须“小于等于”k3。
  • 如果存在两个关键字,任何一个都不“小于等于”另一个,那么我们称这两个关键字是“等价”的。如果k1“等价于”k2,且k2“等价于”k3,那么k1必须“等价于”k3。

如果两个关键字是等价的(即,任何一个都不“小于等于”另一个),那么容器将它们视作相等来处理。当用作map的关键字时,只能有一个元素与这两个关键字关联,我们可以用两者中任意一个来访问对应的值。


重载关键字类型代替<运算符

在定义关联容器时,必须用尖括号指出要定义哪种类型的容器,自定义的比较类型必须在尖括号中紧跟着元素类型给出。

上面说到,可以用一个具有严格弱序性质的函数来起到<运算符的作用,但是函数名并不是一个类型,它仅仅只是一个名字。因此需要提供一个指向函数的指针——此时操作类型为函数指针类型。

struct A{int age;
};bool compareA(const A& a1, const A& a2){return a1.age < a2.age;
}int main(int argc, char const *argv[]) {set<A, decltype(compareA)*> Aset(compareA);return 0;
}

上例中,我们使用decltype来指出自定义操作的类型。

当用decltype来获得一个函数指针类型时,必须加上一个*来指出我们要使用一个给定函数类型的指针。 用compareA来初始化Aset对象,这表示当我们向Aset添加元素时,通过调用compareA来为这些元素排序。即,Aset中的元素将按它们的age成员的值排序。可以用compareA代替&compareA作为构造函数的参数,因为当我们使用一个函数的名字时,在需要的情况下它会自动转化为一个指针。 当然,使用&compareIsbn的效果也是一样的。

用typedef定义与compareA相容的函数指针类型,然后用此类型声明set,起到的效果和上面是一样的:

typedef bool (*pf)(const A& , const A&);
set<A, pf> Aset2(compareA);

无序容器

关键字类型

与有序容器使用比较运算符来组织元素不同。无序容器使用关键字类型的==运算符来比较元素,还使用一个hash<key_type>类型的对象来生成每个元素的哈希值。

不能直接定义关键字类型为自定义类类型的无序容器。因为不能向容器那样直接使用哈希模板(如 hash<string>()),必须定义我们自己的hash模板版本。

类似于有序容器重载关键字类型的默认比较操作。无序容器需要提供函数来替代==运算符和哈希值计算函数,格式如下(以unordered_multiset为例):

unordered_multiset<类型名, 重载的哈希函数, 重载的==运算符>

管理桶

无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。为了访问一个元素,容器首先计算元素的哈希值,它指出应该搜索哪个桶。容器将具有一个特定哈希值的所有元素都保存在相同的桶中。如果容器允许重复关键字,所有具有相同关键字的元素也都会在同一个桶中。因此,无序容器的性能依赖于哈希函数的质量和桶的数量和大小。

不同关键字的元素映射到相同的桶也是允许的。当一个桶保存多个元素时,需要顺序搜索这些元素来查找我们想要的那个。计算一个元素的哈希值和在桶中搜索通常都是很快的操作。但是,如果一个桶中保存了很多元素,那么查找一个特定元素就需要大量比较操作。

在这里插入图片描述


关键字相同的元素相邻存储

不论在有序容器还是无序容器中,具有相同关键字的元素都是相邻存储的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态内存、智能指针(shared_ptr、unique_ptr、weak_ptr)、动态数组

文章目录三种对象的分类三种内存的区别动态内存概念智能指针允许的操作智能指针的使用规范new概念内存耗尽/定位new初始化默认初始化直接初始化值初始化delete概念手动释放动态对象空悬指针shared_ptr类格式独有的操作make_shared函数shared_ptr的计数器通过new用普通指针初始化…

动态数组的简单应用

文章目录连接两个字符串字面常量题目注意代码输出结果处理输入的变长字符串题目注意代码连接两个字符串字面常量 题目 连接两个字符串字面常量&#xff0c;将结果保存在一个动态分配的char数组中。重写&#xff0c;连接两个标准库string对象。 注意 使用头文件cstring的str…

二分查找算法实现

文章目录思路代码以二分区间作为while判定条件以给定值作为while判定条件主函数思路 while判定条件的选择&#xff0c;注意最外层的return的内容就好。下面提供了两个代码版本。计算 mid 时需要防止溢出&#xff08;对应类型【如本例中的int】可能存不下&#xff09;&#xff…

根据中序、前序遍历重建二叉树

文章目录题目递归思路细节易错代码复杂度分析迭代思路细节易错代码复杂度分析题目 输入某二叉树的前序遍历和中序遍历的结果&#xff0c;请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 例如&#xff0c;给出 前序遍历 preorder [3,9,20,15,7] 中…

深搜+剪枝

文章目录题目思路注意代码复杂度分析题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c…

搜索+回溯问题(DFS\BFS详解)

文章目录题目思路DFS思路代码复杂度分析BFS思路代码复杂度分析题目 地上有一个m行n列的方格&#xff0c;从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动&#xff0c;它每次可以向左、右、上、下移动一格&#xff08;不能移动到方格外&#xff09;&am…

快速幂实现pow函数(从二分和二进制两种角度理解快速幂)

文章目录迭代实现快速幂思路int的取值范围快速幂从二进制的角度来理解从二分法的角度来理解代码复杂度分析进阶——超级次方思路倒序快速幂正序快速幂代码复杂度分析迭代实现快速幂 实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c;xn&#xff0…

n位数的全排列(需要考虑大数的情况)

文章目录题目思路代码题目 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 示例 1: 输入: n 1 输出: [1,2,3,4,5,6,7,8,9] 说明&#xff1a; 用返回一个整数列表来代替打印 n 为正整数 …

正则表达式匹配(动规)

文章目录题目思路转移方程特征再探 i 和 j代码题目 请实现一个函数用来匹配包含 . 和 * 的正则表达式。模式中的字符 . 表示任意一个字符&#xff0c;而 * 表示它前面的字符可以出现任意次&#xff08;含0次&#xff09;。在本题中&#xff0c;匹配是指字符串的所有字符匹配整…

在循环递增一次的数组中插入元素

文章目录题目思路如何建立左右区间&#xff1f;如何查找最高点&#xff1f;那我们怎么判断 num 到底处于什么样的位置呢&#xff1f;如何确定插入位置&#xff1f;插入元素代码题目 给一个只循环递增一次的数组 res&#xff0c;res 满足首元素大于等于尾元素&#xff0c;形如&…

表示数值的字符串(有限状态自动机与搜索)

文章目录题目思路一代码一思路二代码二题目 思路一 考察有限状态自动机&#xff08;参考jyd&#xff09;&#xff1a; 字符类型&#xff1a; 空格 「 」、数字「 0—9 」 、正负号 「 」 、小数点 「 . 」 、幂符号 「 eE 」 。 状态定义&#xff1a; 按照字符串从左到右的…

树的子结构

文章目录题目深搜深搜代码广搜广搜代码题目 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A: 给定的树 B&#xff1a; 返回 true&#xff0c;因为…

写题过程中碰见的小问题

文章目录和--vector二维vector的初始化数组中最大的数max_element()数组所有元素之和accumulate()vector数组去重对pair类型的vector排序对元素都为正整数的vector利用sort默认的升序排列进行降序排列一维数组转二维数组size_t和int如何不用临时变量交换两个数?将类函数的形参…

LeetCode——二叉树序列化与反序列化

文章目录题目思路问题一问题二代码实现题目 请实现两个函数&#xff0c;分别用来序列化和反序列化二叉树。 设计一个算法来实现二叉树的序列化与反序列化。不限定序列 / 反序列化算法执行逻辑&#xff0c;你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序…

jsp中生成的验证码和存在session里面的验证码不一致的处理

今天在调试项目的时候发现&#xff0c;在提交表单的时候的验证码有问题&#xff0c;问题是这样的&#xff1a;就是通过debug模式查看得知&#xff1a;jsp页面生成的验证码和表单输入的页面输入的一样&#xff0c;但是到后台执行的时候&#xff0c;你会发现他们是不一样的&#…

求1~n这n个整数十进制表示中1出现的次数

文章目录题目思路代码复杂度分析题目 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;那么1&#xff5e;12这些整数中包含1 的数字有1、10、11和12。可得1一共出现了5次。 思路 将个位、十位……每位…

求数字序列中的第n位对应的数字

文章目录题目思路代码复杂度分析致谢题目 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中&#xff0c;第5位&#xff08;从下标0开始计数&#xff09;是5&#xff0c;第13位是1&#xff0c;第19位是4&#xff0c;等等。 请写一个函数&#xff0c…

一学就废的归并排序

文章目录其他与排序有关的文章原理代码实现复杂度分析其他与排序有关的文章 一学就废的三种简单排序【冒泡、插入、选择】 原理 归并排序&#xff08;Merge sort&#xff09;&#xff1a; 归并排序对元素 递归地 进行 逐层折半分组&#xff0c;然后从最小分组开始&#xff0c…

树状数组的相关知识 及 求逆序对的运用

文章目录树状数组概念前缀和和区间和树状数组原理区间和——单点更新前缀和——区间查询完整代码离散化sort函数unique函数去重erase函数仅保留不重复元素通过树状数组求逆序对树状数组概念 树状数组又名二叉索引树&#xff0c;其查询与插入的复杂度都为 O(logN)&#xff0c;其…

二叉搜索树相关知识及应用操作

文章目录概念查找二叉搜索树的第k大节点概念 二叉查找树&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又名&#xff1a;二叉搜索树&#xff0c;二叉排序树&#xff09;——它或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a; 若它的…