【剑指offer】寻找长度为n,值范围[0,n-1]的数组中第一个重复的数字(C++)


2020秋招提前批开始了?莫慌,一定要把剑指offer多刷几遍。

题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

测试用例

给定数组{2,3,1,0,2,5,3}和数组长度7;
返回值true,并将第一个重复的数字2赋值给int *duplication。

解题方案

Solution1:哈希+遍历

根据值范围为0~n-1的特点,创建一个长度为n的bool类型哈希数组hash,初始化为false;从头遍历原始数组,将遍历到的数字的在hash数组中的值修改为true;遍历过程中遇到hash数组中已经为true的数字即为首个重复数字。

class Solution {
public:
	//哈希+遍历
    //判断一个数组中是否含有重复的数字
    bool duplicate(int numbers[], int length, int* duplication) {
        //创建一个下标为0~n-1的哈希数组,初始化为0,从头遍历,更新每个的计数,当遇到计数大于1的,返回true
        vector<bool> hash(length, false);
        for(int i=0; i<length; ++i){
            if(hash[numbers[i]]){
                *duplication = numbers[i];
                return true;
            }
            else{
                hash[numbers[i]] = true;
            }
        }
        return false;
    }
};

Solution2:in-place算法

参考自:牛客网官方题解

方法一中的一个条件我们没有用到。也就是数据的范围是0-n-1。所以我们可以这么做:
设置一个指针i指向开头0,对于arr[i]进行判断,
(1)如果arr[i] == i, 说明下标为i的数据正确的放在了该位置上,让i++;
(2)如果arr[i] != i, 说明没有正确放在位置上,那么我们就把arr[i]放在正确的位置上,也就是交换;
(3)arr[i] 和arr[arr[i]]。交换之后,如果arr[i] != i, 继续交换;
(4)如果交换的过程中,arr[i] == arr[arr[i]],说明遇到了重复值,返回即可。

	//in-place算法
    bool duplicate(int numbers[], int length, int* duplication) {
        int temp;
        for(int i=0; i<length; ++i){
            //当i位置元素不是i时,将numbers[i]交换到numbers[numbers[i]]
            while(numbers[i]!=i){
                //在numbers[i]的位置已有该元素时,证明该数字重复
                if(numbers[i]==numbers[numbers[i]]){
                    *duplication = numbers[i];
                    return true;
                }
                //swap(i, numbers[i]);
                else{
                    temp = numbers[i];
                    numbers[i] = numbers[temp];
                    numbers[temp] = temp;
                }
            }
        }
        return false;
    }

Solution3:in-place算法plus

参考自:牛客网评论区(ID:王大爷)

用原始一维数组来保存两种信息 怒赞!
不需要额外的数组或者hash表来保存,题目保证数组里数字的范围在0 ~ n-1之间,所以可以利用现有数组设置标志,当一个数字i被访问过后,可以设置对应位numbers[i]上的数 + length(因为该对应位+length可以通过%length得到原始值,故不需要担心数组元素被更改),之后再遇到相同的数时,会发现对应位上的数已经大于等于n了,那么直接返回这个数即可。

	//只用原始一维数组来保存两种信息,tql,膜拜
    bool duplicate(int numbers[], int length, int* duplication) {
        for(int i=0;i!=length;++i){
            //得到原始数组中numbers[i]位置的值
            int index=numbers[i]%length;
            //当numbers[index]>=length表示之前已经遍历过index这个数字,因此重复
            if(numbers[index]>=length){
                *duplication=index;
                return true;
            }
            //遍历到的每个数字index,都在numbers[index]的位置+length
            //既能表示index出现过,又不会丢失原始数组numbers[index]的信息(%length)
            numbers[index]+=length;  
        }
        return false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值