链接:217. 存在重复元素 - 力扣(LeetCode)
题目描述
给你一个整数数组 nums
。如果任一值在数组中出现 至少两次 ,返回 true
;如果数组中每个元素互不相同,返回 false
。
示例 1:
输入:nums = [1,2,3,1] 输出:true
示例 2:
输入:nums = [1,2,3,4] 输出:false
提示:
1 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
思路题点
通过审题发现只需要判断某个元素是否重复出现即可。最简单的就是采用两个for循环进行遍历,但时间复杂度为O(n^2)。考虑用排序和哈希表实现。
一、排序
于是又想到可以先排序,再遍历相邻元素是否相同。排序算法可以采用c++内置的sort(nums.begin(),nums.end())方法,注意std::sort
函数接受的是迭代器,而不是数组元素本身。
c++代码实现:
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
std::sort(nums.begin(),nums.end());//排序,默认升序
for(int i=0;i<nums.size()-1;++i){//遍历
if(nums[i]==nums[i+1]){//找到重复元素,返回true
return true;
}
}
return false;
}
};
也可用桶排序。
- 创建一个大小与数组长度相等的桶(数组)。
- 遍历数组中的每个元素,将元素作为索引,将对应桶中的值加1。
- 同时判断,如果有任何一个桶的值大于1,则说明数组中存在重复元素。
- 最后如果没有任何一个桶的值大于1,则说明数组中没有重复元素。
class Solution{
public:
bool hasDuplicate(const std::vector<int>& nums) {
//获取元素最大值,以便确定桶的大小
int max_value = *max_element(nums.begin(), nums.end());
std::vector<int> bucket(max_value + 1, 0); //赋初值0
//遍历同时判断
for (int num : nums) {
if(++bucket[num]>1){ //存在重复元素
return true;
}
}
return false;
}
};
上述代码不适用于数组中为负值的情况。修改如下:
- 首先,找到数组中的最小值和最大值,确定桶(数组)的大小。可以使用
std::min_element
和std::max_element
来找到最小值和最大值。 - 根据最小值和最大值,计算桶的大小,即为最大值减去最小值加一。创建一个大小为桶大小的桶数组,并初始化为0。
- 遍历数组中的每个元素,将元素减去最小值得到偏移量,并将对应偏移量的桶计数加1。
- 判断,如果有任何一个桶的计数大于1,则说明数组中存在重复元素。
- 最后,如果没有任何一个桶的计数大于1,则说明数组中没有重复元素。
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
//获取最大值和最小值
int min_value = *min_element(nums.begin(), nums.end());
int max_value = *max_element(nums.begin(), nums.end());
//求得桶的大小
int bucket_size = max_value - min_value + 1;
//赋初值
std::vector<int> bucket(bucket_size, 0);
for (int num : nums) {
int offset = num - min_value;//通过偏移量找到桶
if(++bucket[offset]>1){
return true;
}
}
return false;
}
};
很不幸,呜呜呜~:(不合适于数组长度很大且数组元素的范围很广的情形)
二、哈希表
对于数组中每个元素,可以将它插入到哈希表中。如果插入一个元素时发现该元素已经存在于哈希表中,则说明存在重复的元素。
c++代码实现:
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_set<int> s;//创建无序集合即哈希表
for (int x: nums) {//c++11特有的遍历写法
//使用find,返回的是被查找元素的位置,没有则返回set.end()。
if (s.find(x) != s.end()) {
return true;
}
s.insert(x);//将元素插入哈希表
}
return false;
}
};
相关知识
1、 sort函数
C++ STL sort函数是基于快速排序实现的,默认升序。
//按照指定的 comp 排序规则,对 [first, last) 区域内的元素进行排序,第三个参数不选则为默认 void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
示例题:
c++实现:
#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
struct E{
char name[101];
int age;
int score;
}buf[1000];
//分数,姓名,年龄按照升序进行排列
bool cmp(E a, E b)
{
if (a.score != b.score)
return a.score < b.score;
int tmp = strcmp(a.name,b.name);
//对于strcmp函数,strcmp(str1,str2),相等时返回0,str1 > str2时返回正数,否则返回负数
if (tmp != 0)
return tmp < 0;
else
return a.age < b.age;
}
int main()
{
int N;
while (scanf("%d", &N) != EOF)
{
for (int i = 0; i < N; i++)
scanf("%s%d%d",buf[i].name,&buf[i].age ,&buf[i].score);
sort(buf,buf + N,cmp);
for (int i = 0; i < N; i++)
printf("%s %d %d\n",buf[i].name,buf[i].age,buf[i].score);
}
return 0;
}
2、std::为c++的标准命名空间
在工程文件中或者我们自己定义的名字,可能会与C++库中的名字发生冲突。名字冲突就是在同一个作用域中有两个或多个同名的实体,为了解决命名冲突 ,C++中引入了命名空间,所谓命名空间就是一个可以由用户自己定义的作用域,在不同的作用域中可以定义相同名字的变量,互不干扰,系统能够区分它们。(而上述代码中用的是内部库函数,所以其实不需要在sort前添加 :: )
3、桶排序介绍
桶排序(Bucket Sort)是一种线性时间复杂度的排序算法,它将数据分散到不同的桶(数组)中,然后对每个桶进行排序,最后将各个桶中的元素按照顺序组合起来。桶排序的时间复杂度为 O(n+k),其中 n
是元素数量,k
是桶的数量。需要注意的是,桶排序的性能高度依赖于元素的分布情况和选择合适的桶大小。
以下是使用 C++ 实现桶排序的示例代码:
#include <iostream>
#include <vector>
#include <algorithm>
// 桶排序函数
void bucketSort(std::vector<int>& arr) {
int n = arr.size();
// 创建n个桶并初始化为空
std::vector<std::vector<int>> buckets(n);
// 将元素放入对应的桶中
for (int i = 0; i < n; i++) {
int bucketIndex = arr[i] / n; // 计算桶索引
buckets[bucketIndex].push_back(arr[i]);
}
// 对每个桶进行排序
for (int i = 0; i < n; i++) {
std::sort(buckets[i].begin(), buckets[i].end());
}
// 将排序后的桶中元素依次放回原数组
int index = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < buckets[i].size(); j++) {
arr[index++] = buckets[i][j];
}
}
}
int main() {
std::vector<int> arr = {39, 53, 12, 93, 75, 21, 56, 42};
// 打印排序前的数组
std::cout << "排序前的数组:" << std::endl;
for (int num : arr) {
std::cout << num << " ";
}
std::cout << std::endl;
// 使用桶排序对数组进行排序
bucketSort(arr);
// 打印排序后的数组
std::cout << "排序后的数组:" << std::endl;
for (int num : arr) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
4、set和unordered_set区别
set中元素是有序递增排列的,底层是用红黑树实现的。而unordered_set中元素是无序的,底层用哈希表实现。
5、set函数介绍
定义set:
1.set<int> s;
2.还可以把一个数组放进去:set<int> s(arr.begin(),arr.end());
定义迭代器:
set<int>::iterator ite;
set常用方法:
- insert() 向set容器中插入元素
- erase() 按元素值删除元素
- begin() 容器中第一个元素的索引,*(s.begin())为该元素的值
- end() 指向容器中最后一个元素的下一位
- rbegin() 容器中最后一个元素的索引
- rend() 容器中第一个元素的索引
- size() set容器的大小
- find() 返回一个指向被查找到元素的迭代器,即被查找元素的位置,没有则返回set.end()。
- count() 返回某个元素出现的个数,如果有,返回1;否则,返回0,因为不存在重复元素
- empty() 容器是否为空,空为true
- clear() 清空容器
6、fill与memset函数的区别
memset函数
- 在头文件<cstring>里面
- 按照字节填充某字符,一般只能填充char型数组,如果填充int型数组,除了0和-1,其他的不能。因为只有00000000 = 0,-1同理,如果我们把每一位都填充“1”,会导致变成填充入“11111111”。
- 如:memset(数组名,0或1,sizeof(数组名) )
fill函数
- 在头文件<algorithm>里面
- 按照单元赋值,将一个区间的元素都赋同一个值,可以给任何类型赋值
- 如int数组:fill(arr, arr + n, 要填入的内容);
- vector也可以:fill(v.begin(), v.end(), 要填入的内容);