更多题目请点链接:《剑指offer》目录索引
1. 找出数组中出现的数字
问题描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了, 也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2, 3, 1, 0, 2, 5, 3}, 那么对应的输出是重复的数字2或者3。
思路
数组中的内容可变,故可采用交换思想,将每一个数字放到对应的位置上
对应的位置指的是下标和数字相等
如果某一数字不在对应位置,即与对应位置进行比较,如果相等故该数字是重复数字,如果不等,将二者进行交换
代码:
int CountRange1(int numbers[], int length)
{
//判断数组和长度合法性
if (numbers == NULL || length < 0)
return -1;
//判断数组内容是否合法
int i = 0, j = 0;
for (i= 0; i < length; ++i)
{
if (numbers[i] < 1 || numbers[i] > length)
{
break;
}
}
while(j<length)
{
while (numbers[j]!=j)
{
//检查对应位置上是不是
if (numbers[numbers[j]] == numbers[j])
{
return numbers[j];
}
//不是,交换两个数字
int tmp = numbers[j];
numbers[j] = numbers[tmp];
numbers[tmp] = tmp;
}
++j;
}
return -1;
}
2. 不修改数组内容,找出数组中重复出现的数字
题目描述:
在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至
少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的 数组。例如,如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或者3。
思路:
数组中数字的范围都是1~n,可将其查找范围划分成1~n/2,n/2~n进行查找,然后在将n/2缩小为n/2/2……依次类推,直至缩小范围为1个数字
查找过程中,统计查找范围内的数字个数count,如果count大于查找范围n(count>n),说明此范围内至少有一个重复的数字
查找到最后一个数字时,如果count大于1,说明该数字就是重复的数字
int getDuplication(const int* numbers, int length)
{
//数组为空,或长度小于0,不存在
if (numbers == NULL || length < 0)
return -1;
//数组内容的范围是1~length-1
int min = 1;
int max = length - 1;
/*采用二分查找的思想,将1-n分为两部分,1--n/2,n/2--n*/
//当min和max为同一值时,表示范围为1,检测最后一个小区间是否是重复的
while (min <= max)
{
int mid = min + (max - min) / 2;
int count = countRange(numbers,length,min,mid);
//如果最后一个数字是重复数字,则统计出的count肯定大于1
if (min == max)
{
if (count > 1)
return min;
else
break;
}
//如果count大于n,表示至少有一个重复数字
if (count > (mid - min) + 1)
max = mid;
else
min = mid + 1;
}
return -1;
}
//统计1-n范围内有几个数字,如果count > n,则表示1-n范围内至少有一个重复数字
int countRange(const int* numbers, int length, int start, int end)
{
if (numbers == NULL)
return 0;
int count = 0;
for (int i = 0; i < length; ++i)
{
if (numbers[i] >= start && numbers[i] <= end)
++count;
}
return count;
}
void Test()
{
int arr[] = { 1, 2, 2, 6, 4, 5, 6 };
cout << getDuplication(arr, sizeof(arr) / sizeof(int)) << endl;
}