# 剑指offer 面试题(3)

本文探讨了在限定范围内的整数数组中查找重复数字的有效算法。介绍了三种方法:暴力匹配、排序查找和哈希映射,以及一种空间复杂度为1的高级技巧。通过实例代码展示了每种方法的实现细节和时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组中重复的数字

题意

在长度为n的数组里的所有数字都在0~n-1的范围内。数组中有些数字是重复的,但不知道有几个是重复的,也不知道重复了几次。请找出数组中任意一个重复的数字。

分析

一开始没注意读题,他只让找到一个任意的重复的数字即可。最暴力的方式是枚举每一个数,看看数组中有没有其他的数与之相同。

这里的代码中,duplicate函数为solve函数,有三个 参数,numbers表示输入的数组,length表示输入的数组的长度,duplication为输出的重复的那个值,如果输入是正确的,则返回true,否则返回false

#include <algorithm>
using namespace std;
class Solution {
public:
	// Parameters:
	//        numbers:     an array of integers
	//        length:      the length of array numbers
	//        duplication: (Output) the duplicated number in the array number
	// Return value:       true if the input is valid, and there are some duplications in the array number
	//                     otherwise false
	bool duplicate(int numbers[], int length, int* duplication) {
		for (int i = 0; i < length; i++)
			for (int j = 0; j < length; j++)
				if (i != j && numbers[i] == numbers[j])
				{
					*duplication = numbers[i];
					return true;
				}
		return false;
	}
};

这种做法时间复杂度为O(n2)O(n^2)O(n2),当然还有更快的方法,我们可以选择排序,然后从头到尾扫一遍,如果有相同的,那么他们肯定是相邻的。

#include <algorithm>
using namespace std;
class Solution {
public:
	bool duplicate(int numbers[], int length, int* duplication) {
		std::sort(numbers, numbers + length);
		bool flag = 0;
		for (int i = 1; i < length; i++)
		{
			if (numbers[i] == numbers[i - 1])
			{
				*duplication = numbers[i];
				return true;
			}
		}
		return false;
	}
};

这里的排序用的快排,所以时间复杂度为O(n∗logn)O(n*logn)O(nlogn), 除此之外还可以用hash的方法快速判断之前是否扫过一个数。这里用map来保存

class Solution {
public:
	bool duplicate(int numbers[], int length, int* duplication) {
		map<int, int> mp;
		bool flag = 0;
		for (int i = 0; i < length; i++)
			mp[numbers[i]]++;
		for (int i=0;i<length;i++)
			if (mp[numbers[i]] > 1)
			{
				*duplication = numbers[i];
				return true;
			}
		return false;
	}
};

书上还介绍了一种稍微复杂一点的方法,但是可以将空间复杂度降到1,因为题目告诉了所有的数字都在n-1的范围内,所以如果没有重复的话,必然是0,1,2…n-1这n个数,如果我们将其排好序,则numbers[i] == i,也就是说他的下标和数值是相等的。那么我们可以从第一个数开始,将每个数换到他排好序后的位置。比如第i个数m,如果i==m,则表示第i个数位置是对的,所以继续看i+1个数,是否也是正确的位置。如果不等,那么表示m原来不该在i这个位置,而应该在m这个位置,所以我们将m与下标为m的数字交换。这样一来m这个下标与m这个数也对应了。
再看换过来的这个数,如果他正好等于了i,那么也就是相当于换好了,否则重复上述操作。最后如果有重复的数字的话,应该是这样的情况:当前位置为i,数字为m,且m不等于i,即m不在正确的位置,当我们要将m换到正确的位置时,发现了m这个位置就是m,即m是重复的,所以我们只要在交换两个数的位置之前判断一下即可发现是否有重复。

这里我们要加一点限制,来判断输入是否合法。在之前的代码中,我们认为只有没有重复的数字,输入才算不合法。但是这里还要看数组中数字的值是否都在0~n-1,还有长度是否大于0,数组指针是否为空等。

class Solution {
public:
	bool duplicate(int numbers[], int length, int* duplication) {
	    if (length <=0 || numbers == nullptr) return false; 
	    for (int i = 0; i < length; i++)
	    	if (numbers[i] <= 0 || numbers[i] >=length-1) return false;
		for (int i = 0; i < length; i++)
		{
			while (numbers[i] != i)
			{
				if (numbers[numbers[i]] == numbers[i])
				{
					*duplication = numbers[i];
					return true;
				}
				swap(numbers[i], numbers[numbers[i]]);
			}
		}
		return false;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值