《剑指 offer》:数组中重复的数字

   

目录

     题目一:找出数组中重复的数字

    题目二:不修改数组找到重复的数字。


 题目一:找出数组中重复的数字

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


    例如:如果输入长度为 7 的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字 2 或 3。

    方法一:最简单的方法就是先把输入的数组排序,然后遍历数组,找到重复的数。排序一个长度为 n 的数组需要的时间复杂度为 O(nlogn)。

    代码实现

void BubbleSort(int numbers[], int sz)
{
	int i = 0;
	int j = 0;

	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (numbers[j] > numbers[j + 1])
			{
				int tmp = numbers[j];
				numbers[j] = numbers[j + 1];
				numbers[j + 1] = tmp;
			}
		}
	}
}

void FindDuplicateNumber(int numbers[], int sz)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz; i++)
	{
		if (numbers[i] == numbers[i + 1])
		{
			int count = 2;
			int duplication = numbers[i];
			for (j = i + 2; j < sz; j++)
			{
				if (duplication == numbers[j])
				{
					count++;
				}
				if (duplication != numbers[j])
				{
					i = j - 1;
					j = sz + 1;
				}
			}
			printf("重复数字为:%d , 此重复数字重复次数为:%d \n", duplication, count);
		}
	}
}

void test()
{
	int numbers[] = { 2,3,1,0,2,5,3};
	int sz = sizeof(numbers) / sizeof(numbers[0]);
	BubbleSort(numbers, sz);
	FindDuplicateNumber(numbers, sz);
}

     运行结果: 

 

方法二:方法二没有把所有重复的数字都打印出来,就好像方法一,只打印出重复的数字,并没有返回重复值。故此查找函数中只判断是否存在重复值。
      利用哈希表解决。

      首先,有两种情况下一定没有重复的值。
        一:数组为空;
        二:因为数组中的数字都在 0 ~ n-1 之间,如果这个数组中没有重复的数字,那么当数组排序之后数字 i 将出现在下标为 i 的位置。
            故当有重复数字时,上面是不成立的。

      现在重排这个数组。从头到尾依此扫描这个数组。当 遍历到下标为 i 时,记numbers[i] = m;首先比较 m 是不是等于 i 。==> 如果是,说明,这个数字可以排在这儿,接着遍历下一个;如果不是,则拿下标为 m 和第 m 个数比较。==>    
       如果相等,说明找到一个重复的数字(该重复的数字在下标为 i 和 m 的位置出现);如果不相等,就交换位置。 ==>  重复此过程。

:返回值表示数组中是否存在重复的数字。当输入的数组中存在重复的数字时,返回 true ; 否则返回 false。
       每个数字最多只要交换两次就能找到属于自己的位置,因此时间复杂度为 O(n)
        所有的操作不走都是在输入数组上进行的,不许哟啊额外的分配内存,所以空间复杂度为 O(1)

int FindDuplicateNumber(int numbers[], int sz, int* duplication)
{
	//数组为空
	if (numbers == NULL || sz <= 0)
	{
		return 0;
	}

	//数组元素不在规定范围内
	for (int i = 0; i < sz; ++i)
	{
		if (numbers[i]<0 || numbers[i]>sz - 1)
		{
			return 0;
		}
	}

	for (int i = 0; i < sz; ++i)
	{
		//判断是否存在重复的数字
		while (numbers[i] != i)
		{

			if (numbers[i] == numbers[numbers[i]])
			{
				*duplication = numbers[i];
				return 1;
			}

			//swap numbers[i] and numbers[numbers[i]]
			int temp = numbers[i];
			numbers[i] = numbers[temp];
			numbers[temp] = temp;

		}
	}
	return 0;
} 

void test()
{
	int number[] = { 2,3,1,0,2,5,3 };
	int sz = sizeof(number) / sizeof(number[0]);
	int duplication = 0;
	if (FindDuplicateNumber(number, sz, &duplication) == 1)
	{
		printf("%d\n", duplication);
	}
	else
	{
		printf("没有重复的数字\n");
	}
}

运行结果: 

 

    题目二:不修改数组找到重复的数字。

        在一个长度为 n+1 的数组里的所有数字都在 1 ~ n 的范围内,所有数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。    
        例如:如果输入长度为 8 的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字 2 或 3。

    题一与题二的区别:
        (1)不能修改数组;
        (2)题二不用判断是不是存在重复的数字,而是一定存在,找到存在的是谁

    思路:根据第一题,我们很容易就可以想到两种办法,一个就是遍历查找;一个是创建一个数组,用来代替我们在原数组上修改。
        但是这两种办法,第一个时间复杂度较大,第二个需要 O(n) 的辅助空间。
          那么,分析一下本题,首先根据这道题数组的数据范围,可得一定存在重复数字。原因在于:假如没有重复的数字,那么
        1 ~ n 的范围内最多只能有 n 个数字,而目前有 n+1 个数字,说明一定存在重复的数字。
          因此,我们这道题可以利用二分查找算法的思想
    
    方法:将从 1 ~ n 的数字从中间数字 m 分成两部分,哪部分的元素个数大于 m ,说明重复的数字在哪部分。

    时间复杂度:O(nlogn)   空间复杂度:O(1)  。此算法无法找出所有重复的数字。

int getDuplication(const int* numbers, int sz)
{
	int start = 1;
	int end = sz - 1;

	//数组为空
	if (numbers == NULL || sz <= 0)
	{
		return -1;
	}

	//二分
	while (end >= start)
	{
		int middle = ((end - start) >> 1) + start;
		int count = countRange(numbers, sz, start, middle);   //统计二分后区域元素个数
		if (end == start)
		{
			if (count > 1)
			{
				return start;
			}
			else
			{
				break;
			}
		}

		//判断重复的数字在二分后哪一部分
		if (count > (middle - start + 1))
		{
			end = middle;
		}
		else
		{
			start = middle + 1;
		}
	}
	return -1;
}

//统计区域元素个数
int countRange(const int* numbers, int sz, int start, int end)
{
	int count = 0;
	int i = 0;

	//数组为空
	if (numbers == NULL)
	{
		return 0;
	}

	for (i = 0; i < sz; i++)
	{
		if (numbers[i] >= start && numbers[i] <= end)
		{
			++count;
		}
	}

	return count;
}

void test()
{
	const int numbers[] = { 2,3,5,4,3,2,6,7 };
	int sz = sizeof(numbers) / sizeof(numbers[0]);
	int ret = getDuplication(numbers, sz);
	printf("重复的数字为:%d\n", ret);
}

运行结果: 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值