C++ set 和 multiset 容器

set/multiset容器概念

set和multiset是一个集合容器,其中set所包含的元素是唯一的,集合中的元素按一定的顺序排列。set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。在n个数中查找目标数的效率是 log2 n

红黑树定义 — 是每个节点都带有颜色属性(颜色为红色或黑色)的自平衡二叉查找树.

满足下列性质:

  1. 节点是红色或黑色;
  2. 根节点是黑色;
  3. 所有叶子节点都是黑色节点(NULL);
  4. 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
  5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
    C++ set 和 multiset 容器_第1张图片

Set 和 multiset 特点

  1. set中元素插入过程是按排序规则插入,所以不能指定插入位置。
  2. set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
  3. multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中同一值可以出现多次。
  4. 不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素
  5. 头文件#include

以上笔记都来自于腾讯课堂骑牛学院


按我个人理解,只需要知道:

  1. set容器不能存储重复的元素,即每个元素都是独一无二的;且无论按什么顺序存储,底层代码都会将他们循序存储!
  2. multiset容器可以存储重复的元素。且无论按什么顺序存储,底层代码都会将他们循序存储!

如果想了解它底层是如何排序的,请点击下面链接,会有详细解释:
https://blog.csdn.net/cpp_learner/article/details/104730577

由于set 和 multiset 的用法都是一样的,所以下面的示例以set为例子说明!

说明:STL里的容器用法都是差不多的,所以一些函数用法和其他容器用法都是一的就不详细解析了,有兴趣了解的可以点下面:

vector容器
deque容器
list容器

特别推荐:
容器和对象的BUG


定义
  1. 不带参数
// 无参
set<int> s1;
set<double> s2;
set<Student> s3; // 类
  1. 带参数
// 有参
set<int> s3(s1.begin(), s1.end());	// 迭代器法
set<int> s4 = s1;					// 赋值运算符重载
set<int> s5(s3);					// 拷贝构造函数

// 交换元素
s1.swap(s2);
例:

set<int> s1, s2;

s1.insert(1);
s2.insert(2);

s1.swap(s2);

// 在容器中插入元素5
s1.insert(5);
// insert的返回值:
因为set容器只能存储唯一的元素,所以,当我们插入重复的元素时会报错,我们可以使用pair记录他的返回值,并把它打印出来。

pair<set<int>::iterator, bool> ret = s1.insert(5);	// 返回值类型:pair::iterator, bool>
cout << "第一个迭代器:" << *(ret.first) << endl;
cout << "第二个布尔值:" << ret.second << endl;

返回两个值:第一个是当前元素的迭代器,第二个布尔值bool;
所以可以使用 .first获取迭代器,使用 .second获取布尔值。

//返回容器中第一个数据的迭代器
s1.begin();

// 返回容器中最后一个数据之后的迭代器
s1.end();

// 返回容器中倒数第一个元素的迭代器
s1.rbegin();

// 返回容器中倒数最后一个元素的后面的迭代器
s1.rend();

// 使用迭代器方式输出容器中的元素

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

// 使用迭代器方式逆序输出容器中的元素

// 逆序输出
for (set<int>::reverse_iterator it = s1.rbegin(); it != s1.rend(); it++) {
	cout << *it << " ";
}
cout << endl;

// 返回容器中元素的数目
s1.size();

具体示例可以查看下面测试代码 函数test4
注意事项: 它们没有resize 方法

// 判断容器是否为空
s1.empty();

// 删除迭代器所指的元素,返回下一个元素的迭代器
s1.erase(s1.begin());

// 删除区间[beg,end)的所有元素,返回下一个元素的迭代器
s1.erase(Begin, End); // 下面测试代码中会有代码示例 函数test5

// 删除容器中值为6的元素
s1.erase(6);

// 清除所有元素
s1.clear();

// 返回查找元素的迭代器位置
s1.find(3);
cout << "s1.find --> " << *(s1.find(3)) << endl;

// 返回容器中查找的值的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1
s1.count(4);
cout << "s1.count --> " << s1.count(4) << endl;

// 返回第一个 >= 该元素的迭代器
s1.lower_bound(5);
cout << "s1.lower_bound --> " << *(s1.lower_bound(5)) << endl;

// 返回第一个 > 该元素的迭代器
s1.upper_bound(5);
cout << "s1.upper_bound --> " << *(s1.upper_bound(5)) << endl;

// 返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,
// 如[beg,end)。以上函数返回两个迭代器,而这两个迭代器被封装在pair中。

pair<set<int>::iterator, set<int>::iterator> itii = s1.equal_range(1);
cout << "第一个迭代器:" << *(itii.first) << endl;
cout << "第二个迭代器:" << *(itii.second) << endl;

注意:
// 1, 2, 3, 4, 5, 6
// s1.equal_range(1);
// 返回值的第一个迭代指向自己本身
// 返回值的第二个迭代器指向下一个迭代器,也即是2的迭代器,相当于 ==> […)

// 假如元素的值是:1, 1, 1, 1, 2, 3, 4, 5, 6
// 那么: mulitset.equal_range(1)
// 返回值的第一个迭代指向自己本身
// 返回值的第二个迭代器指向下一个迭代器,也即是2的迭代器,相当于 ==> […)
// 所以是包含了一个区间[1, 1, 1, 1, 2);


测试代码:

#include 
#include 
#include 

using namespace std;

// set&mulitset的定义
void test1(void) {
	// 无参
	set<int> s1;
	set<double> s2;

	for (int i = 0; i < 5; i++) {
		s1.insert(10 - i);
	}

	// 有参
	set<int> s3(s1.begin(), s1.end());
	set<int> s4 = s1;
	set<int> s5(s3);

	s4.insert(999);
	// 交换元素
	s4.swap(s5);

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


// insert元素插入与pair返回值
void test2(void) {
	set<int> s1;

	//for (int i = 0; i < 5; i++) {
	//	s1.insert(i + 1);
	//}

	//pair::iterator, bool> ret = s1.insert(4);	// 返回值类型:pair::iterator, bool>
	//cout << "第一个迭代器:" << *(ret.first) << endl;
	//cout << "第二个布尔值:" << ret.second << endl;


	for (int i = 0; i < 5; i++) {
		pair<set<int>::iterator, bool> ret = s1.insert(i);
		if (ret.second) {
			cout << "插入成功!" << endl;

		} else {
			cout << "插入失败!" << endl;
		}
	}


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

// set与迭代器
void test3(void) {
	set<int> s1;

	
	s1.begin();      //返回容器中第一个数据的迭代器。
	s1.end();       //返回容器中最后一个数据之后的迭代器。
	s1.rbegin();   //返回容器中倒数第一个元素的迭代器。
	s1.rend();    //返回容器中倒数最后一个元素的后面的迭代器。

	s1.insert(5);     //在容器中插入元素。
	s1.insert(4);
	s1.insert(3);
	s1.insert(2);
	s1.insert(1);

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

	// 逆序输出
	for (set<int>::reverse_iterator it = s1.rbegin(); it != s1.rend(); it++) {
		cout << *it << " ";
	}
	cout << endl;

}

// set/multiset的大小
void test4(void) {
	set<int> s1;

	s1.insert(5);
	s1.insert(4);
	s1.insert(3);
	s1.insert(2);
	s1.insert(1);

	s1.size();	//返回容器中元素的数目
	s1.empty();//判断容器是否为空

	cout << "s1的元素个数:" << s1.size() << endl;
	if (!s1.empty()) {
		cout << "容器不为空!" << endl;

	} else {
		cout << "容器为空!" << endl;
	}

}

// set/multiset的删除
void test5(void) {
	set<int> s1;

	s1.insert(6);
	s1.insert(5);
	s1.insert(4);
	s1.insert(3);
	s1.insert(2);
	s1.insert(1);


	s1.erase(s1.begin());     //删除pos迭代器所指的元素,返回下一个元素的迭代器。

	set<int>::iterator Begin = s1.begin();
	Begin++;
	set<int>::iterator End = s1.end();
	End--;

	s1.erase(Begin, End);	  //删除区间[beg,end)的所有元素,返回下一个元素的迭代器。

	s1.erase(6);     //删除容器中值为6的元素。

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


	s1.clear();		 // 清除所有元素
}

// set/multiset的查找
void test6(void) {
	set<int> s1;

	s1.insert(6);
	s1.insert(5);
	s1.insert(4);
	s1.insert(3);
	s1.insert(2);
	s1.insert(1);

	// 返回查找元素的迭代器位置
	s1.find(3);
	cout << "s1.find --> " << *(s1.find(3)) << endl;

	// 返回容器中查找的值的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
	s1.count(4);
	cout << "s1.count --> " << s1.count(4) << endl;

	// 返回第一个 >= 该元素的迭代器
	s1.lower_bound(5);
	cout << "s1.lower_bound --> " << *(s1.lower_bound(5)) << endl;

	// 返回第一个 > 该元素的迭代器
	s1.upper_bound(5);
	cout << "s1.upper_bound --> " << *(s1.upper_bound(5)) << endl;

	// 返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,
	// 如[beg,end)。以上函数返回两个迭代器,而这两个迭代器被封装在pair中。
	pair<set<int>::iterator, set<int>::iterator> itii = s1.equal_range(1);
	cout << "第一个迭代器:" << *(itii.first) << endl;
	cout << "第二个迭代器:" << *(itii.second) << endl;
	// 1, 2, 3, 4, 5, 6
	// s1.equal_range(1);
	// 返回值的第一个迭代指向自己本身
	// 返回值的第二个迭代器指向下一个迭代器,也即是2的迭代器,相当于 ==> [...)

	// 假如元素的值是:1, 1, 1, 1, 2, 3, 4, 5, 6
	// 那么: mulitset.equal_range(1)
	// 返回值的第一个迭代指向自己本身
	// 返回值的第二个迭代器指向下一个迭代器,也即是2的迭代器,相当于 ==> [...)
	// 所以是包含了一个区间[1, 1, 1, 1, 2);
}

int main(void) {
	//test1();

	//test2();

	//test3();

	//test4();
	
	//test5();

	test6();

	system("pause");
	return 0;
}

总结:
set 和 multiset 容器就已经解析完了,其实用法并不难,STL所有容器用法都差不多,会了一个容器的使用,其他容器也是百通的,所以请认真学习!

你可能感兴趣的:(c++,c++)