文章目录
- 左闭右开原则
- 示例:
- 示例中erase的用法
- 不能写成s.erase(s.begin()+left)的原因
- STL中支持随机访问的迭代器
左闭右开原则
在 C++ 中,容器(如 vector
,set
,map
等)的迭代器都遵循左闭右开的原则。
也就是说,对于任何容器 c
,c.begin()
返回的迭代器指向容器的第一个元素,而 c.end()
返回的迭代器指向容器的“尾后”元素,也就是最后一个元素的下一个位置。
所以, c.end()
不指向任何有效元素,不能解引用得到元素,通常只作为范围结束的标志。
例如,下面的代码是遍历一个 vector
的标准方法:
vector<int> v = {1, 2, 3, 4, 5};
for(auto it = v.begin(); it != v.end(); ++it) {
cout << *it << endl;
}
在这个例子中,v.begin()
返回的迭代器指向第一个元素 1
,v.end()
返回的迭代器指向尾后位置。我们一直迭代到 it
等于 v.end()
时停止,这时 it
已经不再指向任何有效元素。这就是左闭右开原则在 C++ 容器中的应用。
如果我们需要访问最后一个元素,应该用**反向迭代器*s.rbegin()**来实现。
示例:
//leetcode 6911.不间断子数组
class Solution {
public:
long long continuousSubarrays(vector<int> &nums) {
long long ans = 0;
multiset<int> s;
int left = 0;
for (int right = 0; right < nums.size(); right++) {
s.insert(nums[right]);
while (*s.rbegin() - *s.begin() > 2){
s.erase(s.find(nums[left]));
left++;
}
ans += right - left + 1;
}
return ans;
}
};
s.rbegin()
返回的是一个反向迭代器,它指向的是容器中的最后一个元素。反向迭代器的工作方式和普通迭代器正好相反:递增反向迭代器会移动到前一个元素,递减反向迭代器会移动到后一个元素。因此,s.rbegin()
实际上给了我们一个方便的方式来访问容器中的最后一个元素。
在上面这个例子中,由于multiset是一个自动排序的set容器,因此**s.begin()
和 s.rbegin()
分别返回指向 multiset
中最小元素和最大元素的迭代器**。因为这些方法返回的是迭代器,所以我们需要使用 *
操作符来解引用迭代器,得到它们实际指向的值。
示例中erase的用法
在 C++ 的 STL 中,erase
是用来删除容器中的元素的成员函数,multiset
的 erase
函数可以接收两种类型的参数:
- 一个值:
erase(val)
会删除multiset
中所有等于val
的元素。 - 一个迭代器:
erase(iterator)
会删除迭代器指向的元素。
比如,在示例中,s.erase(s.find(nums[left++]))
的 erase
函数接收的是一个迭代器。
find
函数在 multiset
中寻找给定值的元素,如果找到,返回指向这个元素的迭代器;如果没有找到,返回 s.end()
。然后 s.erase
删除这个迭代器指向的元素。
在示例代码中,使用迭代器而不是值作为参数,是因为我们只想删除窗口最左边的元素,而不是删除所有等于这个值的元素。如果直接使用值作为参数,s.erase(nums[left++])
就会删除所有等于 nums[left++]
的元素。所以需要使用 find
函数找到最左边的元素的迭代器,并传递给 erase
函数。
不能写成s.erase(s.begin()+left)的原因
在使用vector容器的时候,迭代器支持随机访问的操作。例如下图:
但在 std::multiset
中,不能这么使用。
因为**std::multiset
中的迭代器只支持单向(forward)或双向(bidirectional)的操作,而不支持随机访问**。也就是说,不能直接加一个偏移量到 begin()
。会出现下图报错。
如果想从 std::multiset
的 begin()
开始移动到某个位置,需要使用迭代器的 ++
操作,比如:
auto it = s.begin();
std::advance(it, left); // 或者使用 for 循环 left 次执行 ++it
s.erase(it);
以上代码中,std::advance
函数可以用于在输入迭代器上进行前进操作。但注意这个操作的复杂度是线性的,因为 std::multiset
的迭代器不支持随机访问,所以这可能会影响算法效率。
在不支持随机访问的容器内,最直接的删除元素方式:
set1.erase(set1.find(a));//set中删除元素a,使用find先得到迭代器
STL中支持随机访问的迭代器
在C++的标准模板库(STL)中,std::vector
,std::deque
,std::array
,和std::string
是支持随机访问的容器,它们的迭代器都支持随机访问。
这意味着可以直接在这些容器的迭代器上加上一个偏移量来访问元素。以下是一些示例:
1. std::vector
:
std::vector<int> v = {1, 2, 3, 4, 5};
auto it = v.begin() + 2; // 现在 it 指向元素 3
v.erase(it); // 删除元素 3
2. std::deque
:
std::deque<int> d = {1, 2, 3, 4, 5};
auto it = d.begin() + 2; // 现在 it 指向元素 3
d.erase(it); // 删除元素 3
3. std::array
:
std::array<int, 5> a = {1, 2, 3, 4, 5};
auto it = a.begin() + 2; // 现在 it 指向元素 3
// array 不支持 erase 操作,但你可以通过迭代器访问元素
4. std::string
:
std::string s = "hello";
auto it = s.begin() + 2; // 现在 it 指向字符 'l'
s.erase(it); // 删除字符 'l'
需要注意的是,不是所有的容器都支持随机访问。例如 std::list
,std::set
,std::map
等容器的迭代器只支持前向或双向访问,不能在这些迭代器上直接加上一个偏移量。同样,这些容器中的 erase
函数通常也接受一个迭代器参数,但这个迭代器不能通过加偏移量来得到。