問題來源:Find All Numbers Disappeared in an Array
好久沒有刷題了,感受大腦開始遲鈍,因此決定重拾刷題的樂趣。一開始不要太難,選一些經過率高的題目作,而後就看到了這個題目。我有些吃驚,這個題我雖然知道兩種解法,但自己仍是有難度的,竟然經過率這麼高。而後就搜索相關網頁,看到一個和它很接近的題目《Find All Duplicates in an Array》,而後就釋然了。這兩個題目有相同的題幹,只是問題略微不一樣,解法有類似之處。估計是由於題號太接近了,會作第一個以後,第二個也很容易就作對了。本文主要解析《Find All Numbers Disappeared in an Array》,順便簡單解釋一下《Find All Duplicates in an Array》。
該題的含義是:給定一個長度爲n的數組,數組元素是1~n。可是有些元素出現一次,有些元素出現兩次,從而也會致使有些元素不出現。如今讓咱們找到哪些元素沒有出現。另一個題目是讓咱們找到出現兩次的元素。時間複雜度O(n),空間複雜度O(1)。數組
第一種方法是利用以前博客《數組統計分析的另外一種方法》介紹的方法—元素歸位法。元素歸位法很容易理解,就是將n個元素交換到它應該在的位置。例如,元素5就放到位置4(下標從0開始)。這裏須要注意一點,將某個元素交換到正確位置可能會致使當前位置的元素還不在正確位置,須要繼續交換直到不能交換爲止,僞代碼以下:markdown
for i=1:n
while canSwap(i) do swap(i);
將元素歸位以後,咱們就很容易得到哪些元素沒有出現。當某個位置不是正確元素的時候,就意味着這個元素沒有出現。也即針對沒有出現的元素,咱們只須要返回下標;針對出現兩次的元素,咱們只須要返回該位置的值。
這裏有一個疑問,僞代碼有兩個for循環,複雜度是否是O(
C語言版代碼以下:app
void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
int* result=(int*)malloc(sizeof(int)*numsSize);
for(int i=0;i<numsSize;i++)
{
while(nums[i]!=i+1&&nums[nums[i]-1]!=nums[i])
{
swap(&nums[i],&nums[nums[i]-1]);
}
}
*returnSize=0;
for(int i=0;i<numsSize;i++)
{
if(nums[i]!=i+1)
{
result[*returnSize]=i+1;
*returnSize=*returnSize+1;
}
}
return result;
}
在待字閨中公衆號《數組統計分析》中,陳利人老師曾經給出另一種解法—取餘法,道理也比較好理解。數組的元素範圍爲1~n,第一次循環首先把每一個元素對應的位置加上(n+1);第二次循環把每一個位置除以(n+1),若是該位置爲0,表示某個元素沒有出現;若是該位置等於2,表示出現兩次。
原理是什麼呢?在第一次循環中,咱們實際上是將每一個位置變成k*(n+1)+i,其中k表示該位置加(n+1)的次數,取值爲0、一、2,i表示該位置原本的元素。在第二次循環中,由於i的範圍是1~n,因此除以(n+1)就等於0,從而咱們就得到了k的值。根據k的值,咱們就很容易知道哪些元素沒有出現,哪些元素出現了屢次。
C語言版代碼以下:ui
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
int* result=(int*)malloc(sizeof(int)*numsSize);
*returnSize=0;
for(int i=0;i<numsSize;i++)
{
nums[nums[i]%(numsSize+1)-1]+=(numsSize+1);
}
for(int i=0;i<numsSize;i++)
{
if(nums[i]/(numsSize+1)==0)
{
result[*returnSize]=i+1;
*returnSize=*returnSize+1;
}
}
return result;
}
在Top Solution中,有網友分享了一種很奇妙的解法—取負法。含義是:將元素對應的位置取負。簡單一句話可能很差理解,咱們舉個例子。假設在位置k放了元素i,則在取負的過程當中i的取值有兩種可能:爲正,表示當前還沒有遇到元素k將該位置取負;爲負,表示當前已經有元素k出現,並將元素取負。可是咱們不關心k,咱們關心元素i。元素i既然出現,咱們就看一下位置i:爲正,表示這是元素i第一次出現,咱們將位置i取負;爲負,表示元素i已經出現過一次,咱們不作任何操做。無論一個元素出現一次仍是兩次,只要出現它對應的位置就會被取負。當某個元素不出現的時候,該元素對應的位置始終訪問不到,因此仍是正值,經過這種方法咱們就能夠找到哪些元素沒有出現。
經過上面的分析咱們也很容易知道,在取負的過程當中,若是發現要取負的位置已經爲負,說明這個元素已經出現過,也即該元素出現了兩次,咱們能夠將該元素保留下來。
C語言版代碼以下:spa
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
int* result=(int*)malloc(sizeof(int)*numsSize);
for(int i=0;i<numsSize;i++)
{
int index=abs(nums[i])-1;
if(nums[index]>0) nums[index]=-nums[index];
}
*returnSize=0;
for(int i=0;i<numsSize;i++)
{
if(nums[i]>0)
{
result[*returnSize]=i+1;
*returnSize=*returnSize+1;
}
}
return result;
}
針對《Find All Duplicates in an Array》,採用取負法實現的C語言代碼以下:.net
int* findDuplicates(int* nums, int numsSize, int* returnSize) {
int* result=(int*)malloc(sizeof(int)*numsSize);
*returnSize=0;
for(int i=0;i<numsSize;i++)
{
int index=abs(nums[i])-1;
if(nums[index]>0)
{
nums[index]=-nums[index];
}
else
{
result[*returnSize]=index+1;
*returnSize=*returnSize+1;
}
}
return result;
}