26.删除有序数组中的重复项
方法1:使用STL(不完全符合原地修改的要求)
虽然这种方法更简洁,但它并不完全符合题目的“原地”要求,因为它可能在底层使用额外的空间。然而,了解如何利用C++的标准库来简化代码还是很有用的。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
nums.erase(unique(nums.begin(), nums.end()), nums.end());
return nums.size();
}
};
在C++中,nums.erase(unique(nums.begin(), nums.end()), nums.end());
这行代码利用了标准模板库(STL)中的unique
和erase
函数来删除数组(或向量)nums
中的重复元素。这个表达式包含两个部分,理解起来需要拆分开来看:
std::unique(nums.begin(), nums.end())
:std::unique
是一个STL算法,它接收一个序列的开始和结束迭代器作为参数(在这里是nums.begin()
和nums.end()
,代表nums
向量的起始和结束位置)。- 它的作用是重排输入序列(
nums
向量),使得每个元素只出现一次且仅在其第一次出现的位置,而所有重复的元素被移动到序列的未尾。 std::unique
不实际改变容器的大小;它返回一个迭代器,指向重排后的序列中“新的”逻辑结束位置,也就是第一个重复元素被移动到的位置。
在C++标准库中,std::unique
函数是用来移除来自已排序的序列(例如数组或向量)中的重复元素的。std::unique
函数通常与 std::sort
函数结合使用,因为它只能在已排序的序列上正确地移除重复项。
这个函数的作用是将重复的元素移动到序列的尾部,并返回一个指向新不重复序列尾部的迭代器。需要注意的是,它并不真正删除容器的元素,而是返回了一个指向容器中不重复序列新的逻辑结尾的迭代器。在这之后,可以使用容器的 erase
方法来删除重复的元素。
STL(Standard Template Library,标准模板库)算法是C++标准库的一部分,提供了一系列定义在头文件 、 和部分其他头文件中的模板函数,用于执行各种常见的算法操作,如排序、搜索、变换和计算等。STL算法作用于容器和其他序列上,通过迭代器与容器解耦,从而能够在不同类型的容器上执行相同的操作。
STL算法的特点包括:
泛型编程:STL算法通过模板实现,使得同一个算法可以用于不同类型的数据。
与容器无关:算法通过迭代器与容器进行通信,使得同一个算法可以应用于任何支持适当迭代器类型的容器上。
高效灵活:STL算法经过优化,提供了执行各种操作的高效方式。它们还允许通过函数对象和lambda表达式进行定制。
只读、非修改和修改算法:STL算法根据它们对元素的操作分为只读算法(不修改序列中的元素)、非修改算法(可能改变元素的顺序但不改变值)和修改算法(改变元素的值)。
一些常用的STL算法包括:
排序和搜索:std::sort、std::stable_sort、std::partial_sort、std::binary_search、std::lower_bound等。
变换:std::transform、std::replace、std::replace_if等。
数值操作:std::accumulate、std::inner_product、std::adjacent_difference等,这些定义在头文件中。
分区和排列:std::partition、std::nth_element、std::next_permutation等。
查找和比较:std::find、std::find_if、std::equal、std::mismatch等。
删除和替换:std::remove、std::remove_if、std::unique等。
nums.erase(iterator1, iterator2)
:erase
是std::vector
的成员函数,它删除位于两个迭代器iterator1
和iterator2
之间的元素,包括iterator1
指向的元素,但不包括iterator2
指向的元素。- 在这个表达式中,
iterator1
是std::unique
返回的迭代器(指向重排后序列的“新”结束位置),而iterator2
是nums.end()
。 - 通过调用
erase
,向量nums
实际上删除了所有重复的元素,即从std::unique
返回的迭代器到原序列末尾的所有元素。
因此,整个表达式nums.erase(unique(nums.begin(), nums.end()), nums.end());
实现了删除nums
向量中所有重复元素的目的,并且修改了向量的大小,使其仅包含唯一元素。这是一种简洁且高效地在STL兼容容器中删除重复元素的方法。但需要注意的是,这个方法假定nums
已经是排序的,因为std::unique
只能删除连续的重复元素。如果nums
未排序,则需要先进行排序。
第一次回忆
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
nums.erase(nums.unique(nums.begin(),nums.end()),nums.end())
return nums();
}
};
GPT修改
#include <vector>
using namespace std;
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
// 正确的使用 unique,并将返回的迭代器作为 erase 的起点
nums.erase(unique(nums.begin(), nums.end()), nums.end());
// 返回修改后的数组的大小,而不是调用 nums()
return nums.size();
}
};
unique
是std::vector
类型没有的成员函数,应该是std::unique
,并且它属于<algorithm>
头文件,所以使用时需要确保已经包含了这个头文件。unique(nums.begin(), nums.end())
返回一个迭代器,指向去重后容器新的逻辑末尾,erase
函数接受两个迭代器作为参数,表示要删除元素的范围,您的调用方式是正确的,但括号使用有误。- 返回值应该是
nums.size()
,用于返回数组的长度,而不是nums()
,后者看起来像是在尝试调用nums
作为一个函数,这是不正确的。
此外,请确保你的代码文件开头包含了 <vector>
和 <algorithm>
头文件,因为 std::vector
类型定义在 <vector>
中,而 std::unique
函数定义在 <algorithm>
中。
方法2:使用额外的标记数组(不推荐)(我第一次想到的是这样)
这种方法使用一个额外的数组来标记哪些元素是唯一的。虽然这种方法可以工作,但它不满足题目的原地修改要求,并且效率低于双指针法。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if (nums.empty()) return 0;
vector<int> uniqueNums;
uniqueNums.push_back(nums[0]);
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] != nums[i - 1]) {
uniqueNums.push_back(nums[i]);
}
}
nums = uniqueNums;
return nums.size();
}
};
方法3:双指针法(原地修改)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if (nums.empty()) return 0;
int k = 1;
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] != nums[i - 1]) {
nums[k++] = nums[i];
}
}
return k;
}
};
这段代码首先检查数组是否为空。如果不为空,它初始化k
为1,因为至少第一个元素是唯一的。然后,它遍历数组,每次遇到与前一个元素不同的元素时,就将该元素复制到k
指针的位置,并将k
增加1。遍历结束后,k
就是数组中唯一元素的数量,同时数组的前k
个元素就是这些唯一元素,它们保持了原始顺序。这个方法只需要一次遍历,所以时间复杂度是O(n),其中n是数组nums
的长度。