剑指OFFER_03_1_寻找数组中重复的数字_JAVA实现
题目:寻找数组中重复的数字
- 在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
题设条件中值得关注的点
-
数字的范围被数组长度所约束。这意味着如果一个数组中没有重复的数字,将其从小到大排序,得到的数组将会是一个数组下标和值相同的数组。如这样的:{0,1,2,3,4,5,6}
-
题目暂时不需要将所有重复的数字都给列出来,所以只要检测到一个重复的数字,将其输出即可。
书中的算法思想
如果将一个数组按从小到大的顺序排序,一定可以完成任务,但是我们不需要将整个数组排序,只需要在排序的过程中发现第一个重复的数就可以了。
- 设置 i = 0
- 从数组的下标为 i 的元素开始检测,如果 i = nums.length,则跳到第5步。
- 如果当前元素的值和数组下标相同,意味着当前下标的值已经找到,可以向后检索,i++,回到第2步。
- 如果当前元素的值和数组下标不同,将当前的值与当前的值作为数组下标的值比较,若不同则交换(如nums[0]=3,则将nums[0]与nums[3]的值交换)并返回第2步。若相同则将当前值返回,因为已经出现了重复的元素,过程全部结束。
- 到达此步,说明整个数组已经整理完毕,并且没有出现重复的数字,所以返回 -1;
用例解析
选题目中给的用例就可以理解这个思想:
数组取{2,3,1,0,2,5,3}
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
nums[] | 2 | 3 | 1 | 0 | 2 | 5 | 3 |
nums[] | 1 | 3 | 2 | 0 | 2 | 5 | 3 |
nums[] | 1 | 3 | 2 | 0 | 2 | 5 | 3 |
nums[] | 3 | 1 | 2 | 0 | 2 | 5 | 3 |
nums[] | 3 | 1 | 2 | 0 | 2 | 5 | 3 |
nums[] | 0 | 1 | 2 | 3 | 2 | 5 | 3 |
nums[] | 0 | 1 | 2 | 3 | 2 | 5 | 3 |
nums[] | 0 | 1 | 2 | 3 | 2 | 5 | 3 |
nums[] | 0 | 1 | 2 | 3 | 2 | 5 | 3 |
nums[] | 0 | 1 | 2 | 3 | 2 | 5 | 3 |
nums[] | 0 | 1 | 2 | 3 | 2 | 5 | 3 |
检查nums[0],值为2,将nums[0]=2与nums[2]=1比较,值不同,交换。
检查nums[0],值为1,将nums[0]=1与nums[1]=3比较,值不同,交换。
检查nums[0],值为3,将nums[0]=3与nums[3]=0比较,值不同,交换。
此时nums[0]=0,i++,检查下一个。
检查nums[1],值为1,i++,检查下一个。
检查nums[2],值为2,i++,检查下一个。
检查nums[3],值为3,i++,检查下一个。
检查nums[4],值为2,将nums[4]=2与nums[2]=2比较,值相同,出现重复值,返回nums[4]或者nums[2]均可(因为值相同)
代码
函数主体部分代码
package q01;
public class Question01 {
//空的构造函数
public Question01() {
}
/**
* 判断numbers数组中是否存在重复出现的数,若有则存入duplication 返回true,否则返回false
* @param nums 待检测数组
* @param length numbers数组的长度
* @param duplication 存放重复出现的数的数组
* @return
*/
public int duplicate(int[] nums)
{
int result = -1;
//若待检测数组为空,或长度<0,一定是有错误,应该return -1,表示异常;
if(nums == null || nums.length <= 0)
{
return result;
}
int length = nums.length;
//根据题目要求,数组中存放的数是在0~n-1之间的,若有数不在此范围,应该return -1,表示异常;
for (int i = 0; i < nums.length; i++) {
if(nums[i] < 0 || nums[i] > length - 1)
{
return result;
}
}
//依次检索数组中的元素
for(int i = 0; i < nums.length; i++)
{
//持续对当前元素进行操作,直到值与下标相同或者发现重复元素为止
while (nums[i] != i)
{
//若值与以此值为下标的值相同,则返回该值
if (nums[i] == nums[nums[i]])
{
return nums[i];
}
//若不同,则交换
int temp = nums[i];
nums[i] = nums[nums[i]];
//此行注释掉的代码是我一开始写的时候犯的低级错误,写出来提醒一下各位。
//因为此时的nums[i]已经发生了改变,不能再用nums[nums[i]]去调用了,否则会出现死循环。
//nums[nums[i]] = temp;
nums[temp] = temp;
}
}
//返回默认的结果0,表示没有重复
return 0;
}
}
测试部分代码
package q01;
public class TestApp {
public static void main(String[] args) {
Question01 e = new Question01();
//空数组,检测应为-1
int[] nums1 = null;
//数组中存在负数,检测应为-1
int[] nums2 = {2,1,5,4,-2,3};
//数组中存在数大于array.length-1,检测应为-1
int[] nums3 = {3,2,5,2};
//符合题目要求的数组,应当返回0,
int[] nums4 = {2,1,3,0,4};
//符合题目要求的数组,应当返回2
int[] nums5 = {2,1,3,0,2};
int result1 = e.duplicate(nums1);
System.out.println("result1: " + result1);
int result2= e.duplicate(nums2);
System.out.println("result2: " + result2);
int result3 = e.duplicate(nums3);
System.out.println("result3: " + result3);
int result4 = e.duplicate(nums4);
System.out.println("result4: " + result4);
int result5 = e.duplicate(nums5);
System.out.println("result5: " + result5);
}
}