剑指Offer--051-数组中重复的数字
2016-05-30 18:32
369 查看
链接
牛客OJ:数组中重复的数字九度OJ:未收录
GitHub代码: 051-数组中重复的数字
CSDN题解:剑指Offer–051-数组中重复的数字
牛客OJ | 九度OJ | CSDN题解 | GitHub代码 |
---|---|---|---|
051-数组中重复的数字 | 未收录 | 剑指Offer–051-数组中重复的数字 | 051-数组中重复的数字 |
题意
题目描述在一个长度为n的数组里的所有数字都在0到n-1的范围内。
数组中某些数字是重复的,但不知道有几个数字是重复的。
也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
样例输入
2, 3, 1, 0, 2, 5, 3
2, 1, 3, 1, 4
样例输出
2
1
检查数据的合法性
检查输入参数是否合法数组中的数据是否满足所有数字都在0到n-1的范围内
bool CheckValidity(int *numbers, int length) { // 输入数据不合法 if(numbers == NULL || length <= 0) { return false; } // 元素必须在[0, n-1]的范围 for(int i = 0; i < length; i++) { if(numbers[i] < 0 || numbers[i] > length - 1) { return false; } } return true; }
排序后判断重复
最简单的思路就是先把输入的数组排序。从排序的数组中找出重复的数字就是个很容易的事情了。只需要从头向尾扫描一遍排序好的数组即可。对一个数组排序的时间复杂度是O(nlogn)
扫描一个排序好的数组发现重复的数字的时间复杂度是O(n)
class Solution { public: bool duplicate(int numbers[], int length, int* duplication) { *duplication = -1; if(CheckValidity(numbers, length) == false) { return false; } sort(numbers, numbers + length); // 对数组进行排序 int i = 0; bool isDup = false; *duplication = -1; // 扫描一遍数组发现重复的数字 for(i = 0; i < length - 1; i++) { if(numbers[i] == numbers[i + 1]) // 重复了 { isDup = true; *duplication = numbers[i]; break; } } return isDup; } };
hashtable哈希法优化查找速率
当然我们用hashtabble也可以实现,hashtable中可以很方便的查找到某个元素是否存在,时间复杂度O(n),空间复杂度O(n)
class Solution { public: // Parameters: // numbers: an array of integers // length: the length of array numbers // duplication: (Output) the duplicated number in the array number // Return value: true if the input is valid, and there are some duplications in the array number // otherwise false bool duplicate(int numbers[], int length, int* duplication) { *duplication = -1; if(CheckValidity(numbers, length) == false) { return false; } set<int> s; bool isDup = false; *duplication = -1; for(int i = 0; i < length; i++) { if(s.count(numbers[i]) == 1) // 如果当前元素已经存在set中 { isDup = true; *duplication = numbers[i]; break; } s.insert(numbers[i]); } return isDup; } };
标识数组法
hashtable的方式是在优化查找的过程,因为hashtable查找的时间复杂度为O(1),那么我们继而联想到,如果不设hash,直接用一个用一个数组flag当前标识,记录某个元素是否出现,
flag[i] =0标识元素i未出现,
flag[i] =1标识元素i出现
这样也可以在O(1)的时间内实现判断
空间O(n)的标识数组
class Solution { public: bool duplicate(int numbers[], int length, int* duplication) { *duplication = -1; if(CheckValidity(numbers, length) == false) { return false; } int flags[length]; memset(flags, 0, sizeof(flags)); for(int i = 0; i < length; i++) { if(flags[numbers[i]] != 0) { *duplication = numbers[i]; return true; } else { flags[numbers[i]] = 1; } } return false; } };
我们采用了一个以空间换时间的方式, 但是有没有什么办法能够优化空间上的消耗呢?
因为我们需要的只是一个标识, 0或者1,那么我们完全没必要一个整数来做标识,
因此可做如下优化
用一个n位的单元来存储即可, 每一位都是一个标识
这种方法可以把空间消耗将低, 但是还是需要额外的空间,那么有没有不需要空间消耗的方法呢?
符号位标识法
我们可以看到数组中元素的大小都在[0-n)这个区间内, 都是正数,那么他们的符号位对我们来说就是无关紧要的, 因此我们直接拿符号位当成我们的标识位就行了numbers[i]符号位为0表示数字i没有重复
numbers[i]符号位为1标识数字i重复
#define SET_SYMBOL_BIT(num) ((num) |= (1 << 31)) /* 设置符号位为1 */ #define GET_ORIGIN_NUM(num) ((num) & (~(1 << 31))) /* 获取到源数据 */ #define GET_SYMBOL_BIT(num) (((num) >> 31) & 1) /* 获取符号位(标识)*/ class Solution { public: /* 对于每一个数据numbers[i] 其源数据origin = GET_ORIGIN_NUM(numbers[i])的标识存储在numbers[origin]的符号位 判断其标识是否为1即可 */ bool duplicate(int numbers[], int length, int* duplication) { *duplication = -1; if(CheckValidity(numbers, length) == false) { return false; } for(int i = 0; i < length; i++) { // 当前数字numbers[i]的标识即是numbers[numbers[i]]的符号位 // 检查numbers[i] debug <<"origin = " <<GET_ORIGIN_NUM(numbers[i]) <<", flag = " <<GET_SYMBOL_BIT(numbers[GET_ORIGIN_NUM(numbers[i])]) <<endl; if(GET_SYMBOL_BIT(numbers[GET_ORIGIN_NUM(numbers[i])]) == 1) { *duplication = GET_ORIGIN_NUM(numbers[i]); return true; } else { SET_SYMBOL_BIT(numbers[GET_ORIGIN_NUM(numbers[i])]); debug <<"SET origin = " <<GET_ORIGIN_NUM(numbers[i]) <<", flag = " <<GET_SYMBOL_BIT(numbers[GET_ORIGIN_NUM(numbers[i])]) <<endl; } } return false; } };
固定偏移法
跟标识法类似, 如果不借助外部辅助空间,那么我们只能在数组内部下功夫,又能设置标识,又能恢复数据(不破坏数据)的方式,前面我们用符号位作为标识的方法就是通过符号位,即判断了是否存在,又可以通过符号位的反转重新恢复数据,那么有没有其他类似的方法呢?我们想到我们的数据都是[0, n)这个区间的,那么我们采用类似与移码的方法,让数据加上或者减去一个固定的偏移量,这样就可以即标识数据,又不损坏数据,为了能够区分出数据,这个偏移必须大于N,这样我们的原数据与标识数据存在一一映射关系。
[0, n-1] -=>+偏移n-=> [n, 2n-1]
class Solution { public: // Parameters: // numbers: an array of integers // length: the length of array numbers // duplication: (Output) the duplicated number in the array number // Return value: true if the input is valid, and there are some duplications in the array number // otherwise false bool duplicate(int numbers[], int length, int* duplication) { *duplication = -1; if(CheckValidity(numbers, length) == false) { return false; } for(int i = 0; i < length; i++) { if(numbers[numbers[i] % length] >= length) { *duplication = numbers[i] % length; return true; } else { numbers[numbers[i] % length] += length; } } return false; } };
将元素放在自己该在的位置
剑指offer上提供的方法,这种方法采用交换的方法我们考虑如果每个数字都置出现一次,那么此时是最完美的,每一个下标i对应元素numbers[i],也就是说我们对于数组中的每个元素numbers[i]都把它放在自己应该在的位置上numbers[numbers[i]]上, 如果我们发现有两个元素想往同一个位置上放的时候,说明此元素必然重复
即如下的过程
如果numbers[i] == i, 那么我们认为number[i]这个元素是在自己的位置上的
否则的话, numbers[i]这个元素就应该在numbers[numbers[i]]这个位置上, 于是我们交换numbers[i]和numbers[numbers[i]]
重复操作1, 直到number[i]== i, 则继续操作下一个位置的元素, 或者numbers[i] == numbers[numbers[i],元素重复
class Solution { public: // Parameters: // numbers: an array of integers // length: the length of array numbers // duplication: (Output) the duplicated number in the array number // Return value: true if the input is valid, and there are some duplications in the array number // otherwise false bool duplicate(int numbers[], int length, int* duplication) { *duplication = -1; if(CheckValidity(numbers, length) == false) { return false; } for(int i = 0; i < length; i++) { // 极端情况按顺序排序, 第i个位置上的数numbers[i]应该等于其下标i while(numbers[i] != i) { debug <<"numbers[" <<i <<"] = " <<numbers[i] <<" isn't equal to it's index = " <<i <<endl; // 第numbers[i]位置上的数据numbers[numbers[i]应该是numbers[i] // 即数numbers[i]应该是数组第numbers[i]个数numbers[numbers[i] if(numbers[i] == numbers[numbers[i]]) { *duplication = numbers[i]; return true; } debug <<"swap numbers[" <<i <<"] and numbers[" <<numbers[i] <<"]" <<endl; swap(numbers[i], numbers[numbers[i]]); } debug <<"numbers[" <<i <<"] = " <<numbers[i] <<" is equal to it's index = " <<i <<endl; } return false; } };
相关文章推荐
- 查看github pages文档的方式
- 使用BAE的基于Web.py的简单博客程序
- MySQL 优化
- 只有程序员看的懂的面试圣经|如何拿下编程面试
- 下一次技术面试时要问的 3 个重要问题
- 书评:《算法之美( Algorithms to Live By )》
- Google排名优化的几个影响因素
- 动易2006序列号破解算法公布
- DB2优化(简易版)
- Mysql limit 优化,百万至千万级快速分页 复合索引的引用并应用于轻量级框架
- C#中尾递归的使用、优化及编译器优化
- 对优化Ruby on Rails性能的一些办法的探究
- 优化Ruby脚本效率实例分享
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- Asp编码优化技巧
- 在SQL Server数据库中为标识(IDENTITY)列插入显式值
- 如何监测和优化OLAP数据库
- mysql -参数thread_cache_size优化方法 小结