set
set介绍
1,set的声明如下,T就是set底层关键字的类型
2,set默认要求T⽀持⼩于⽐较,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现仿函数传给第⼆个模版参数
3,set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第三个参 数。
4,⼀般情况下,我们都不需要传后两个模版参数。
5,set底层是⽤红⿊树实现,增删查效率是O(logN) ,迭代器遍历是⾛的搜索树的中序,所以是有序的。
6,前⾯部分我们已经学习了vector/list等容器的使⽤,STL容器接⼝设计,⾼度相似,所以这⾥我们 就不再⼀个接⼝⼀个接⼝的介绍,⽽是直接带着⼤家看⽂档,挑⽐较重要的接⼝进⾏介绍。
7,set是会去重的,所以删除数据时,没有使用bool值,而是int值。
set的构造和迭代器
set的⽀持正向和反向迭代遍历,遍历默认按升序顺序,因为底层是⼆叉搜索树,迭代器遍历⾛的中序;
⽀持迭代器就意味着⽀持范围for,set的iterator和const_iterator都不⽀持迭代器修改数据,修改 关键字数据,破坏了底层搜索树的结构。
// empty (1)
⽆参默认构造
explicit set (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
// range (2)
迭代器区间构造
template <class InputIterator>
set (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& = allocator_type());
// copy (3)
拷⻉构造
set (const set& x);
// initializer list (5) initializer
列表构造
set (initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
//迭代器是⼀个双向迭代器
//正向迭代器
iterator -> a bidirectional iterator to const value_type
iterator begin();
iterator end();
//反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();
set的增删查
find
算法库中实现的查找是迭代器遍历,时间复杂度是O(n)
set内部实现的查找与树的高度有关,时间复杂度是O(logn)
count返回一个数据在set中的个数,但是set会去重,所以返回值是0或1,也是int值。
set<int> s = { 2,3,1,7,1,1,5 };
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
//直接删除值为val的数据,不存在返回0
s.erase(1);
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
//直接查找在利⽤迭代器删除val
auto pos = s.find(3);
if (pos != s.end())
{
s.erase(pos);
}
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
//利⽤count间接实现快速查找
int x = 7;
if (s.count(x))
{
cout << x << "存在" << endl;
}
else
{
cout << x << "不存在" << endl;
}
lower_bound
//返回⼤于等val位置的迭代器
iterator lower_bound (const value_type& val) const;
//返回⼤于val位置的迭代器
iterator upper_bound (const value_type& val) const;
算法库中也有lower_bound,upper_bound,但是要求是要对容器区间排序。
注意所得区间是左闭右开[lower,upper)
如果所给val > set.end();会返回set.end();的值
set<int> myset = { 10,20,30,40,50,60,70,80,90 };
for (auto e : myset)
{
cout << e << " ";
}
cout << endl;
//返回 >= 30位置的迭代器
auto low = myset.lower_bound(30);
//返回 > 70位置的迭代器,如果所给val > myset.end();会返回myset.end();的值
auto up = myset.upper_bound(70);
//使用迭代器打印[30,70)区间的值
auto it = low;
while (it != up)
{
cout << *it << " ";
++it;
}
cout << endl;
//删除[30,70)区间的值
myset.erase(low, up);
for (auto e : myset)
{
cout << e << " ";
}
cout << endl;
multiset和set的差异
使用multiset,头文件依然是#include< set >,不需要其他头文件
multiset不会进行去重操作。
在进行查找操作时,查找到数据是中序遍历的第一个数据。
题目
349. 两个数组的交集 - 力扣(LeetCode)
给定两个数组 nums1
和 nums2
,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
去重:unique(算法库中);set都可以
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// set<int> s1;
// for(auto e : nums1)
// {
// s1.insert(e);
// }
// set<int> s2;
// for(auto e : nums2)
// {
// s2.insert(e);
// }
//迭代器区间构造比较方便
set<int> s1(nums1.begin(),nums1.end());
set<int> s2(nums2.begin(),nums2.end());
vector<int> v;
for(auto e : s1)
{
//在set中count的返回值是1或0,如果s1中的数据在s2中也存在,就是交集
if(s2.count(e))
{
v.push_back(e);
}
}
return v;
}
};
除此之外,还有方法能求交集,当然也可以带入求差集。
并集是直接遍历插入set< >即可,set< >会去重
交集
vector<int> nums1 = { 1,3,5,6,7,8,9 };
vector<int> nums2 = { 1,2,3,4,6 };
vector<int> ret;
//去重+排升序
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
//遍历比较
auto it1 = s1.begin();
auto it2 = s2.begin();
while (it1 != s1.end() && it2 != s2.end())
{
if (*it1 < *it2)
{
++it1;
}
else if (*it1 > *it2)
{
++it2;
}
else
{
//ret.push_back(*it1);
ret.push_back(*it2);
++it1;
++it2;
}
}
for (auto e : ret)
{
cout << e << " ";
}
cout << endl;
差集
vector<int> nums1 = { 1,3,5,6,7,8,9 };
vector<int> nums2 = { 1,2,3,4,6 };
vector<int> ret;
//去重+排升序
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
//遍历比较
auto it1 = s1.begin();
auto it2 = s2.begin();
while (it1 != s1.end() && it2 != s2.end())
{
if (*it1 < *it2)
{
ret.push_back(*it1);
++it1;
}
else if (*it1 > *it2)
{
ret.push_back(*it2);
++it2;
}
else
{
++it1;
++it2;
}
}
//剩余值继续记录
while (it1 != s1.end())
{
ret.push_back(*it1);
++it1;
}
while (it2 != s2.end())
{
ret.push_back(*it2);
++it2;
}
for (auto e : ret)
{
cout << e << " ";
}
cout << endl;
142. 环形链表 II - 力扣(LeetCode)
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
set<ListNode*> s;
ListNode* cur = head;
while(cur)
{
auto ret = s.insert(cur);
if(ret.second == false)
return cur;
cur = cur->next;
}
return nullptr;
}
};