集合与其基本操作
数据结构系列第一章:集合与其基本操作
前言
常说:一个程序 = 算法 + 数据结构
数据结构的重要性可见一斑
这个系列将讲解一些较为重要的数据结构
一、集合
集合作为我们的关联容器的一种,它的优势也有很多
1.集合内无重复:一个集合内,没有相同的数据,依照这个特点,我们可以使用集合来去重,查重
2.集合内有序:一个集合,它的数据是有序的,它支持结构体,可以使用greater<>排序,也可以运算符重载,下文将有所提及
二、集合的使用(c++ STL)
在c++标准模板库内,有集合 set 这个容器
这里就用STL给大家演示
我们可以用它来简化代码
1.头文件
使用集合时,首先要包含这样的一个头文件
#include <set>
2.基本操作
创建集合时,常用的两种创建方法:
1.建立空集(即没有元素在集合内)
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set> //必须包含这个头文件
using namespace std;
int main()
{
set <int> set_1; //定义一个空集
return 0;
}
2.使用数组建立,建立时注意左闭右开
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set> //必须包含这个头文件
using namespace std;
int main()
{
int a[] = {5 , 2 , 3 , 1 , 6 , 2 , 4};
set <int> set_2(a , a + 7); //使用 a 数组定义集合,定义时注意左闭右开
return 0;
}
集合的插入
1.直接插入数据(在输入时处理可以使用)
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set> //必须包含这个头文件
using namespace std;
int main()
{
set <int> set_1;
set_1.insert(4); //在集合中放入 4 这个元素
return 0;
}
2.在指定位置插入数据
注意,这并不会改变集合原来的排序顺序,因为在插入后集合是会更新的
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set> //必须包含这个头文件
using namespace std;
int main()
{
set <int> set_1;
set_1.insert(set_1.begin() , 4); //例:在集合的头部插入 4 ,意义不大,最终还是会进行排序
return 0;
}
集合的删除
这里为大家提供两种操作方法
1.删除指定元素
注意呦,是删除指定的元素,而不是删除这一位上的元素
还是那句话,除首尾外,集合无法使用随机访问器,必须使用迭代器进行访问,具体可以参照输出部分
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set> //必须包含这个头文件
using namespace std;
int main()
{
int a[] = {5 , 2 , 3 , 1 , 6 , 2 , 4};
set <int> set_1(a , a + 7); //使用 a 数组定义集合,定义时注意左闭右开
set_1.erase(set_1.begin()); //删除集合的第一个元素
set_1.erase(5); //删除 5 这个元素,不是删除第五个元素
return 0;
}
2.删除一个区间内的元素(一般在清空集合时使用)
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set> //必须包含这个头文件
using namespace std;
int main()
{
int a[] = {5 , 2 , 3 , 1 , 6 , 2 , 4};
set <int> set_1(a , a + 7); //使用 a 数组定义集合,定义时注意左闭右开
set_1.erase(set_1.begin() , set_1.end()); //删除一个区间的元素
return 0;
}
集合的输出
集合无法使用随机访问器,必须使用迭代器进行访问
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set> //必须包含这个头文件
using namespace std;
int main()
{
int a[] = {5 , 2 , 3 , 1 , 6 , 2 , 4};
set <int> set_1(a , a + 7); //使用 a 数组定义集合,定义时注意左闭右开
/*
我们会发现 a 数组并不是排序后的,还有重复
打印出集合来看看
*/
set <int> :: iterator it; //集合无法随机访问,必须使用迭代器
for(it = set_1.begin() ; it != set_1.end() ; it++)//这里小心踩个坑:结束条件应为 != 不是 < 呦,集合是不能随机访问的
{
printf("%d\n" , *it);//打印数据时迭代器要返回指针
}
}
我们会发现 a 数组并不是排序后的,还有重复
打印出集合来看看是不是会自动排好序和去重呢?
输出后,的确是已经排好序及去重啦~
可能有人会问,那么结构体该如何使用集合排序呢?
这里介绍一种我个人比较喜欢的方法:运算符重载
运算符重载十分方便,尤其是在二分,C++STL及sort等对结构体进行操作时
先来说下运算符重载
struct Node//定义一个结构体
{
int first_data;
int second_data;
}input_data[100];
bool operator < (const Node &a , const Node &b)//运算符重载
{
return (a.first_data == b.first_data ? a.first_data > b.first_data : a.second_data > b.second_data);
}
这样,我们就实现了自己的比较规则,实现重载
运算符重载时,注意这么几个问题:
1.注意,运算符必须与其类型相同,例如 > = < 为bool , + - * 为 int 或long long等
2.括号里的数据,必须要有 const 修饰
3.注意,数据前必须加上地址 & , 表示操作会改变实际结构体的值
只要加上这样的一段运算符重载,就能实现啦~
3.初试身手
我们一起来看下这道题,完善对于集合的理解
时间限制: 1 Sec 内存限制: 128 MB 文件名 set.*
题目描述:
有 N 个正整数,请输出其去重及按升序输出的结果
输入:
第一行,为一个正整数 N , 表示输入数据的个数
以下 N 行,每行一个正整数 Ni,表示一个未排序数据
输出:
多行数据,输出其去重及按升序输出的结果,每行一个正整数
样例输入:
5
4
1
3
3
6
样例输出:
1
3
4
6
说明:
无
数据范围:
N < 10^4
Ni < 10^9
提示:
分析此题,不难发现这和集合的两大特点全都吻合,可以考虑使用集合
标程:
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set>
using namespace std;
int main()
{
set <int> set_data;
int n;
scanf("%d" , &n);
int data;
while(n--)
{
scanf("%d" , &data);
set_data.insert(data);
}
set <int> :: iterator it;
for(it = set_data.begin() ; it != set_data.end() ; it++)
{
printf("%d\n" , *it);
}
return 0;
}
运行结果:
可以看到,答案为正确的
这就是集合的基本操作啦~
总结
今天我们了解了集合这个数据结构,也一起研究了集合的特点及代码实现
以后可以在编程中利用好它的特点优化代码
下章预告:数据结构系列(二) 集合的升级——并查集