一道面试题Lintcode196-Find the Missing Number
http://www.lintcode.com/en/problem/find-the-missing-number/#
Find the Missing Number
Given an array contains N numbers of 0 .. N, find which number doesn't exist in the array.
Given N = 3
and the array [0, 1, 3]
, return 2
.
Do it in-place with O(1) extra memory and O(n) time.
题意:这是一道面试题目,题意是说给你个数组0-n,让你找出里面唯一缺少的那个数。
思路:我的第一思路是二叉堆,但好像南辕北辙了。可以直接利用快排,少于nlgn的时间就可以找到那个数。利用数值与数组下标的对应关系,数组的数排好序后分两段,左端数值与下标相同,右端则数值比下标大一。不需要排序,只需要借助快排,就可以不断的递归二分这个数组,找出那个奇点。
可以参考两篇类似的博文:
http://www.cnblogs.com/jiu0821/p/4138558.html
http://www.cnblogs.com/jiu0821/p/4505480.html
代码:tle
1 class Solution { 2 public: 3 /** 4 * @param nums: a vector of integers 5 * @return: an integer 6 */ 7 int j18(vector<int> &nums,int i,int j){ 8 int x=nums[i]; 9 while(1){ 10 while(i<j&&nums[j]>x) j--; 11 if(i<j) nums[i]=nums[j]; 12 else{ 13 nums[i]=x; 14 return i; 15 } 16 while(i<j&&nums[i]<x) i++; 17 if(i<j) nums[j]=nums[i]; 18 else{ 19 nums[j]=x; 20 return j; 21 } 22 } 23 } 24 int findMissing(vector<int> &nums) { 25 // write your code 26 int i=0,j=nums.size()-1,y; 27 while(y=j18(nums,i,j),1){ 28 if(nums[y]==y) i=y+1; 29 else j=y-1; 30 if(i>j) return i; 31 } 32 } 33 };
之所以tle是因为原题目给有特殊数据,代码里默认快排是x为第一个数,遇到最坏情况,复杂度n*n。下面把x改为最后一个数,就ac了。
ac 279ms
1 class Solution { 2 public: 3 /** 4 * @param nums: a vector of integers 5 * @return: an integer 6 */ 7 int j18(vector<int> &nums,int i,int j){ 8 int x=nums[j]; 9 while(1){ 10 while(i<j&&nums[i]<x) i++; 11 if(i<j) nums[j]=nums[i]; 12 else{ 13 nums[j]=x; 14 return j; 15 } 16 while(i<j&&nums[j]>x) j--; 17 if(i<j) nums[i]=nums[j]; 18 else{ 19 nums[i]=x; 20 return i; 21 } 22 } 23 } 24 int findMissing(vector<int> &nums) { 25 // write your code 26 int i=0,j=nums.size()-1,y; 27 while(y=j18(nums,i,j),1){ 28 if(nums[y]==y) i=y+1; 29 else j=y-1; 30 if(i>j) return i; 31 } 32 } 33 };
当然,这只是投机取巧,也会遇到最坏情况。相对好的方法是取中间或者取随机数。下面是取中间的实现例子:214ms
1 class Solution { 2 public: 3 /** 4 * @param nums: a vector of integers 5 * @return: an integer 6 */ 7 int j18(vector<int> &nums,int i,int j){ 8 int x=nums[(i+j)>>1]; 9 nums[(i+j)>>1]=nums[i]; 10 while(1){ 11 while(i<j&&nums[j]>x) j--; 12 if(i<j) nums[i]=nums[j]; 13 else{ 14 nums[i]=x; 15 return i; 16 } 17 while(i<j&&nums[i]<x) i++; 18 if(i<j) nums[j]=nums[i]; 19 else{ 20 nums[j]=x; 21 return j; 22 } 23 } 24 } 25 int findMissing(vector<int> &nums) { 26 // write your code 27 int i=0,j=nums.size()-1,y; 28 while(y=j18(nums,i,j),1){ 29 if(nums[y]==y) i=y+1; 30 else j=y-1; 31 if(i>j) return i; 32 } 33 } 34 };
最后,说下最直接简单的办法,以空间换时间,加一个bool数组再哈希就可以了。135ms
1 class Solution { 2 public: 3 /** 4 * @param nums: a vector of integers 5 * @return: an integer 6 */ 7 int findMissing(vector<int> &nums) { 8 // write your code 9 int len=nums.size(); 10 bool numb[len+1]; 11 memset(numb,0,sizeof(numb)); 12 for(int i=0;i<len;i++) numb[nums[i]]=true; 13 for(int i=0;i<=len;i++){ 14 if(numb[i]==false) 15 return i; 16 } 17 } 18 };
经好心博友提醒,最优雅的法子横空出世--异或。两个相同的数异或为0。故而把原数组与0-n异或和就为最后的结果。131ms
1 class Solution { 2 public: 3 /** 4 * @param nums: a vector of integers 5 * @return: an integer 6 */ 7 int findMissing(vector<int> &nums) { 8 // write your code 9 int len=nums.size(),x=0; 10 for(int i=0;i<len;i++) x^=nums[i]; 11 for(int i=1;i<=len;i++) x^=i; 12 return x; 13 } 14 };
看到了官方给的答案---借助桶排序。思路是:从0开始遍历到n-1,每次当a[i]!=i的时候,将a[i]与a[a[i]]交换,大于边界的话,就丢掉,直到无法交换位置。那个特殊值有两种情况,一种是在0~n-1之间,那么经过a[i]与a[a[i]]交换,最后以特殊值为下标的位置上一定为n;另一种是特殊值为n,那么a[i]与a[a[i]]交换对于0~n-1都满足。故而只要跟踪边界值n即可,跟踪值x应初始化为n,以对应第二种情况。详情见代码:
1 class Solution { 2 public: 3 /** 4 * @param nums: a vector of integers 5 * @return: an integer 6 */ 7 int findMissing(vector<int> &nums) { 8 // write your code 9 int len=nums.size(),x=len; 10 for(int i=0;i<len;i++){ 11 while(nums[i]!=i){ 12 if(nums[i]==len){ 13 x=i; 14 break; 15 }else{ 16 int tmp=nums[nums[i]];//注意这个地方,不能用三个异或来交换值,因为nums[nums[i]]里的nums[i]是变量。 17 nums[nums[i]]=nums[i]; 18 nums[i]=tmp; 19 } 20 } 21 } 22 return x; 23 } 24 };