不修改数组找出重复的数字

下面有彩蛋

题目描述

给定一个长度为n + 1的整数数组nums,数组中所有的数字都在1~n范围内,其中 n ≥ 1 n \geq 1 n1。请找出数组中任意一个重复的数字,但不能修改输入的数组。

样例
Input: nums = [2,3,5,4,3,2,6,7]
Output: 2或3 

解题思路

  • 普通思路

    • 描述
      遍历整数数组,一一对比,相等则返回。

    • 实现代码:

      /*
      使用到的头文件
      #include <vector>
      */
      
      int duplicateInArray(vector<int> &nums) //传入数组,返回重复的数字
      {
      	for (int i = 0; i < nums.size(); i++) //二重循环遍历
      	{
      		for (int j = i + 1; j < nums.size(); j++)
      		{
      			if (nums[i] == nums[j]) //相等返回该值
      			{
      				return nums[i];
      			}
      		}
      	}
      }
      
    • 复杂度分析

      时间复杂度: O ( n 2 ) O(n^2) O(n2)

      ​ 二重循环需要 n 2 ​ n^2​ n2

      空间复杂度: O ( 2 ) ≈ O ( 1 ) O(2) \approx O(1) O(2)O(1)

      ​ 使用两个临时变量i,j

  • 高级思路

    • 描述

      本题目存在一个特性:数组长度为n + 1,数组数字范围1 ~ n,那么一定存在重复的数(抽屉原理)。

      那么我们可以这样做:将数字范围一分为二,一者数字范围为1 ~ n/2,另一者则是 n/2 + 1 ~ n。将数组数字分派给这两个范围,一定存在其中一个数字范围中数组个数大于 n/2,然后将这个数字范围一分为二,递归下去,就可以找到。

    • 实现代码

      /*
      使用到的头文件
      #include <vector>
      */
      
      int duplicateInArray(vector<int> &nums)
      {
      	int l = 1, r = nums.size() - 1; //以1-n作为二分边界
      	int mid,sum;
      	while (l < r) //循环条件
      	{
      		mid = l + r >> 1; //取中,二分范围为[l,mid] [mid + 1,r]
      		sum = 0; //统计处于[1,mid]范围内数的个数
      
      		for (int i = 0; i < nums.size(); i++)
      		{
      			sum += (nums[i] >= l) && (nums[i] <= mid);
      		}
      
      		if (sum > (mid - l + 1)) //如果数的个数大于[l,mid]区间表示个数,取[l,mid]
      		{
      			r = mid;
      		}
      		else //否则取[mid + 1,r]
      		{
      			l = mid + 1;
      		}
      	}
      	return r; //返回找到的数
      }
      
    • 复杂度分析

      时间复杂度: O ( l o g n ) ​ O(log^n)​ O(logn)

      ​ 就是一次二分查找

      空间复杂度:$O(4) \approx O(1) $

      ​ 使用四个临时变量l,r,mid,sum

二分查找模板

int binarySearch(int l,int r)
{
    while(l < r)
    {
        mid = l + r >> 1; //取中
          
        if(check(mid)) //中值大于等于查找值,取区间[l,mid]
        {
            r = mid;
        }
        else //中值小于于查找值,取区间[mid + 1,r]
        {
            l = mid + 1;
        }
    }
    
    if(equal(l)) //l对应值等于查找值,返回下标
    {
		return l;        
	}
    else //返回-1表示找不到
    {
        return -1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值