找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。
数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
namespace ConsoleApp2;
public class 数组中重复的数字
{
//思路1:拿到一个数,就把他存放到字典里,值当做key,value当位置,然后每一次放入字典前,看看这个数是不是已经存在了,存在就直接返回
//时间是O(n),空间是O(n)
public static int FindRepeatNumber(int[] nums)
{
Dictionary<int, int> dic = new Dictionary<int, int>();
for (int i = 0; i < nums.Length; i++)
{
if (dic.ContainsKey(nums[i]))
{
return nums[i];
}
else
{
dic.Add(nums[i],i);
}
}
return 0;
}
//思路2,考虑到题目是n 的数组 nums 里的所有数字都在 0~n-1 的范围内,这么可以确定,数字可以重复,那么一个索引可以对应多个值
// 想象:打地主,很多农民,很多钱袋(上面印着属于谁),找出谁家有两个钱袋,这些钱袋现在均匀的分在农民手中
//例如[2, 3, 1, 0, 2, 5, 3]
// 怎么快速的找出谁的钱袋多,农民0号,拿出他身上分的钱袋,一看是属于2号的,立马找2号去换,
// 看看2号手中是不是自己的钱袋,如果2号手中已经有自己的钱袋了,那么2号钱袋一定是重复的,这个叼毛就是地主
// 如果2号手中不是自己的钱袋,那么当场进行交换,这样能去确保2号已经有自己的钱袋了,自己拿到了1号钱袋
// 然后0号农民拿着1号钱袋找1号农民交换,操作如上面操作,直到,0号农民拿到了自己的钱袋,那么就进行索引加1,
// 然后1号农民去拿着自己的钱袋去上述操作
// 时间复杂度是0(n),空间是O(1)
public static int FindRepeatNumber2(int[] nums)
{
for (int i = 0; i < nums.Length; )
{
//农民先看看手里的钱袋是不是自己的,是的话直接不管了,下一个农民继续
if (nums[i] == i)
{
i++;
}
//不是自己的钱袋
else
{
//如果自己手里钱袋nums[i]的主人nums[i]手里已经有属于自己的钱袋了nums[nums[i],那么他一定是超过一个钱袋的,是地主
if (nums[nums[i]] == nums[i])
{
return nums[i];
}
//对方手里钱袋不是他自己的,那么与他进行交换,确保对方手里钱袋是自己的
else
{
var temp_data = nums[i];
nums[i] = nums[temp_data];
nums[temp_data] = temp_data;
// 下面是错误示例,nums[i]会在第二行被改变,导致第三行有问题,所以一定要用temp_data
// var temp_data = nums[i];
// nums[i] = nums[nums[i]];
// nums[nums[i]] = temp_data;
}
}
//然后拿着交换来的钱袋继续去找,直到找到属于自己的钱袋(i++),或者找到对方多了一个钱袋(返回结果)。这两个结果必定会出现其中之一
}
return 0;
}
static void Main(string[] args)
{
int num = FindRepeatNumber2(new int[] {1,2,1,1});
Console.WriteLine(num);
}
}