本文参考《剑指Offer》一书,代码采用Java实现。题目一:在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 题目二:在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。 本文的两道题解题思路是不一样的,具体事项见正文。
找出数组中重复数字
题目一
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组 {2, 3, 1, 0, 2, 5, 3} ,那么对应的输出是重复的数字2或者3。
思路
从哈希表的思路拓展,重排数组:把扫描的每个数字(如数字m)放到其对应下标(m下标)的位置上,若同一位置有重复,则说明该数字重复。
(在动手写代码前应该先想好测试用例)
测试用例:
- 数组中带有一个或多个重复数字
- 数组中不包含重复数字
- 无效输入测试用例(空数组、数组数字越界等)
Java代码及复杂度
复杂度:
时间复杂度: O(n)
空间复杂度: O(1)
尽管有两重循环,但是每个数字最多只要交换两次就能找到属于它的位置,因此钟的时间按复杂度是O(n)。
另外所有操作时在输入数组上进行的,不需要分配内存,空间复杂度是O(1)。
含测试代码
1 | public class FindDuplicateNumber1 { |
在同一个类中,与上面的函数拆开的测试代码,为了函数更加简洁。
1 | public class FindDuplicateNumber1 { |
不含测试代码(牛客网提交)
这里的代码为牛客网上通过的代码,如下:
1 | public class Solution { |
不修改数组找出数组中重复数字
题目二
在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或者3。
思路
数组长度为n+1,而数字只从1到n, —说明必定有重复数字—。
可以由二分查找法拓展:把1~n的数字从中间数字m分成两部分,若前一半1~m的数字数目超过m个,说明重复数字在前一半区间,否则,在后半区间m+1~n。每次在区间中都一分为二,知道找到重复数字。
更简单的思路:把该数组看作一个链表,下标代表当前结点,值代表next指针。
测试用例:
- 数组中带有一个或多个重复数字
数组中不包含重复的数字(题目设置必有重复)- 无效输入测试用例(空数组、数组数字越界等)
Java代码
时间复杂度说明:函数countRange()将被调用O(logn)次,每次需要O(n)的时间。
时间复杂度:O(nlogn) (while循环为O(logn),coutRange()函数为O(n))
空间复杂度:O(1)
1 | public class FindDuplicateNumber2 { |
在同一个类中,与上面的函数拆开的测试代码,为了函数更加简洁。
1 | public class FindDuplicateNumber2 { |