217. 存在重复元素--数组--初级算法

链接: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. 创建一个大小与数组长度相等的桶(数组)。
  2. 遍历数组中的每个元素,将元素作为索引,将对应桶中的值加1。
  3. 同时判断,如果有任何一个桶的值大于1,则说明数组中存在重复元素。
  4. 最后如果没有任何一个桶的值大于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;
    }
};

上述代码不适用于数组中为负值的情况。修改如下:

  1. 首先,找到数组中的最小值和最大值,确定桶(数组)的大小。可以使用std::min_elementstd::max_element来找到最小值和最大值。
  2. 根据最小值和最大值,计算桶的大小,即为最大值减去最小值加一。创建一个大小为桶大小的桶数组,并初始化为0。
  3. 遍历数组中的每个元素,将元素减去最小值得到偏移量,并将对应偏移量的桶计数加1。
  4. 判断,如果有任何一个桶的计数大于1,则说明数组中存在重复元素。
  5. 最后,如果没有任何一个桶的计数大于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(), 要填入的内容);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值