0、题目描述
- 一个长度为n的数组中所有数字都在0~n-1之间
- 数组中有些数字是重复的,有些是不重复的
- 不知道每个数字重复几次
- 请找任意一个重复的数字
- 例:长度为7的数组{2、3、1、0、2、5、3}输出2或3
0.1、朴素想法--排序OR桶排
①很容易想到的是先排序,然后判断相邻两个数字之间是否相等。如果相等直接返回true即可。
此时好处是空间复杂度是o1,时间复杂度取决于排序的时间复杂度。
(发散一下复习高级排序算法:堆排序、快速排序)
②可以通过桶排,缺点是空间换时间。
0.2、剑指offer解法理解
这里想写一下自己较为通俗的去理解剑指offer中的解法。
多读几遍题目找一找隐含条件,长度为n且数字必须在0~n-1之间。
逆向的去想一下就可以发现不重复的情况只有一种即:0~n-1的数字都存在且只有一次。
根据这种唯一性可以这样去理解:n个人,n个座位。都有自己的编号,编号对应一个唯一属于自己的座位。
根据书中的例子,有7个座位,编号{0、1、2、3、4、5、6}
当前0号位置上坐的是2;
一号位置上坐的是3;
…
从左向右遍历这个座位,我们可以根据座位号和号码是否相同来区分这个"人"是否"失位"?
0号位置上坐的是2,则2称为失位,我们需要把2送回到2号位上。
当然,如果2号位已经坐了2那么可以直接退出说明2重复了。
换完之后,继续判断0号位是否依然失位。
1、进阶题目描述
进阶题目增加不允许修改原数组为条件。且数字范围1~n之间,不再是0~n-1。
1.1、朴素想法--桶排OR使用STL
与上述相同。桶排空间换时间。
在数据量比较大的时候,用STL中set或者map可以以较小的空间代价完成任务。
2.2、剑指offer解法理解
在这个例子中,数字范围在1~8之间,那么如果有重复的数字必须也是在1~7之间。
思路是通过二分法将区间不断分割。
比如说,第一次分割区间是1~4和5~8两个区间
遍历一次整个数组,如果小于等于4就count++,那么一趟之后我们就可以知道1~4区间中有多少个数字,反之用8(数组的总长度)减去这个count就可知5~8之间有多少个数字。
如果count正好等于区间长度 或者 count大于区间长度,那么这个重复数字可能 或者 一定在这个区间。
继续对这个区间二分。。以此推类。
但很明显,这个算法的弊端是只能找出一个重复数字。
如果有两个重复数字,那么一定会在某个分割阶段,两个区间会割离。
比如{4,4,4,4,8,8,8,8}那么在第一次分割区间的时候只能选择1~4的区间,或者5~8的区间。
而无论你的算法选择哪个区间你都会失去一种情况。