C++ STL库之Vector简介及例题(二)
继“C++ STL库之Vector简介及例题(一)【点击查看】”之后,这篇文章我们继续上一次的介绍,继续对vector的一些算法的函数进行简析及例题分析。
元素操作
删除第2个元素:vec.erase(vec.begin()+1);
删除区间[ i , j )的元素:vec.erase(vec.begin() + i, vec.begin() + j);
删除区间后3个元素:vec.erase(vec.end() - 3, vec.end());
清空vec容器:vec.clear();
其他元素操作遇到会在进行详细解释。
排序
进行数组内排序:sort();
sort()
有三个参数,begin
区间开头(包含),end
区间结尾(不包含),cmp
排序规则(可自定义)。
cmp
参数可不写,默认升序。
#include <iostream>
#include <algorithm> // 包含sort()函数的头文件
#include <vector>
using namespace std;int main()
{vector<int> vec{ 7,5,9,3,2,4 };// 对数组进行升序排序 // 注意:vec.end()指向最后一个元素的后一位sort(vec.begin(),vec.end()); return 0;
}
如果要进行降序排序,我们可以自定义排序规则,例如:
/*头文件略
*/bool cmp(int x, int y) {return x > y; // 自定义降序
}int main() {vector<int> vec{ 9,4,7,1,6 };sort(vec.begin(), vec.end(), cmp); // 第三个参数用自定义的参数for (auto it = vec.begin(); it != vec.end(); ++it) {cout << *it << endl;}return 0;
}
同样的,我们也可以自定义各种不一样的排序方法;
例如:对所有数字的个位数进行升序排序。
/*头文件略
*/bool cmp(int x, int y) {return x % 10 < y % 10; // 对数字取个位
}int main() {vector<int> vec{ 97,412,76,11,699 };sort(vec.begin(), vec.end(), cmp);for (auto it = vec.begin(); it != vec.end(); ++it) {cout << *it << " ";}/*输出结果为: (对个位升序)11 412 76 97 699*/return 0;
}
去重
对vector
进行去重通常涉及排序和unique
算法的组合。以下是进行去重的步骤:
1. 首先对vector
进行排序,以确保所有重复元素都相邻排列;
2. 然后使用std::unique
重排vector
,将重复的元素移到容器末尾;
3. 最后,使用vector
的erase
方法去除重复元素;
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;int main() {vector<int> vec = { 6,6,4,6,2,7,4,1 };sort(vec.begin(), vec.end());// 使用 std::unique 来去除连续重复的元素,它会返回不重复序列的新的逻辑结尾auto last = unique(vec.begin(), vec.end());// 从 vector 中移除重复的元素,resize 容器以移除未定义的尾部元素vec.erase(last, vec.end());for (int it : vec) {cout << it << " ";}return 0;
}
unique
解析:
unique
是一个函数模板,它的作用是去除相邻重复元素。它会重新排列范围内的元素,使得每个元素只出现一次且顺序保持不变,但它并不会改变容器的大小;相反,它会返回一个指向“新的”逻辑末尾的迭代器。
1. std::unique
遍历给定的迭代器范围(通常是一个容器的开始和结束)。
2. 每当它找到一组相邻的重复元素时,它就会把这组重复元素的第一个以外的所有元素移动到范围的末尾。
3. 最后,它返回一个迭代器,指向没有重复元素的序列的新逻辑结尾。
例如,数列{1,2,2,3,3,4,6,7}
,进行unique操作后,数列变成{1,2,3,4,6,7,2,3}
,并且返回一个迭代器指向7
后面的2
。
重要的是要注意,这个“新的逻辑结尾”之后的元素仍然存在于容器中,但它们的值已经是未定义的,并且通常是重复元素的副本。为了真正地删除这些元素,你需要使用容器的erase
方法,它接受两个迭代器作为参数,表示要删除的元素的范围。
例题
力扣26题——删除有序数组中的重复项
给你一个 非严格递增排列 的数组 nums
,请你删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
1. 更改数组 nums
,使 nums
的前 k
个元素包含唯一元素,并按照它们最初在 nums
中出现的顺序排列。nums
的其余元素与 nums
的大小不重要;
2. 返回 k
。
题解
这是一道简单题,主要是为了把这篇文章讲的内容串联起来应用。
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;int result_26(vector<int>& nums) {auto new_end = unique(nums.begin(), nums.end());return distance(nums.begin(), new_end);
}int main() {vector<int> nums = { 0, 0, 1, 1, 1, 2, 2, 3, 3, 4 };cout << result_26(nums);return 0;
}/*
力扣提交代码:
class Solution {
public:int removeDuplicates(vector<int>& nums) {auto new_end = unique(nums.begin(), nums.end());return distance(nums.begin(), new_end);}
};
*/
代码解释
大部分代码都比较简单,在上面都已经写了作用和详细解释,这里主要讲一下新出现的函数distance
:调用 distance
函数计算从 nums
的起始迭代器到 new_end
迭代器的距离。这个距离就是去除相邻重复元素后容器的新长度。
distance
的作用是返回两个迭代器之间的距离,即它们指向的元素之间的元素数量,在计算数组长度的算法题中可以经常被用到。
事件复杂度分析
这段代码主要包含两个部分,std::unique
函数的调用和 std::distance
函数的调用。
1. unique
函数:
对于 vector
,unique
函数通常具有线性时间复杂度,即 O(n),其中 n 是 vector
中的元素个数。这是因为 unique
需要遍历整个 vector
一次,以便重新排列元素,使得每个重复的元素只出现一次,并返回新的逻辑末尾迭代器(也就是新的end(),指向有序不重复数列的后一位)。
2. distance
函数:
distance
函数对于随机访问迭代器具有常数时间复杂度,即 O(1)。然而在这种情况下,distance
的时间复杂度实际上取决于 unique
操作后的新的逻辑末尾和原始 vector
起点之间的距离,但由于这个距离最多与 vector
的原始长度相同,它依然是 O(n)。无论如何,distance
不会增加整个函数的最坏情况时间复杂度。
综合这两个操作,整个 result_26
函数的时间复杂度是线性的,即 O(n)。这里的 n 是传入 vector
的元素数量。
在 main
函数中,result_26(nums)
被调用一次,所以 main
函数的时间复杂度也是 O(n)。