51-【巧解】統計無序陣列各元素出現的次數--時間複雜度O(n),空間複雜度O(1)
一、問題描述
【題型一】
一個長度大小為n的陣列,陣列中的每個元素的取值範圍在[1,n],且為正整數。
問:如何在時間複雜度為O(n),空間複雜度為O(1)的條件下,統計陣列中不同元素出現的次數。
【題型二】
在一個長度為n的數組裡的所有數字都在0-n-1的範圍內。陣列中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出陣列中任意一個重複的數字。
【例如】
如果輸入長度為7的陣列(2,3,1,0,2,5,3),那麼對應的輸出是重複的數字2或者3
二、解題思路
【題型一】
陣列按序掃描,通過當前元素的值作為下標,找到下一個元素。最後得到的陣列中,下標(因為下標從0開始的,故輸出時需要+1)為陣列中出現的元素,每個下標對應的值取反輸出即是該元素出現的頻率。
若當前元素小於0,
則跳過;
若當前元素大於0,
則判斷其作為下標索引到的元素是否大於0,
若大於0,則索引到的元素賦值給當前元素,索引到的元素置為-1;
若小於0,則索引到的元素自減1,當前元素置為0;
用正數/負數來區分a[i]上是原來的值,還是用於計數的值
隨便舉個例子,比如 { 2, 5, 5, 2, 3 }
看到第一個是 2,即2有1個,所以先將這個“1”儲存到a[2-1]的位置上。但a[2-1]有有效數呀,咋辦?移動一下就行。假設我不用負數,而是用中括號來表示計數,步驟依次是
{ 2, 5, 5, 2, 3 }
5, [1], 5, 2, 3
3, [1], 5, 2, [1]
5, [1], [1], 2, [1]
[0], [1], [1], 2, [2]
[0], [2], [1], [0], [2]
結果是 1有0個, 2有2個, 3有1個, 4有0個, 5有2個
此類以值做下標
- 正整數
- 取值範圍[1,n]
- n個數
【題型二】
思路一:排序 -- 時間複雜度O(nlogn)
思路二:雜湊表 -- 時間複雜度O(1) 空間複雜度O(n)
思路三:值為下標 -- 時間複雜度O(n) 空間複雜度O(1)
思路三:以2,3,1,0,2,5,3為例
1)陣列第0號位置為2,2!=0,將0號位置的2與2號位置的1交換,變成 1,3,2,0,2,5,3
2)陣列第0號位置為1,1!=0,將0號位置的1與1號位置的3交換,變成 3,1,2,0,2,5,3
3)陣列第0號位置為3,3!=0,將0號位置的3與3號位置的0交換,變成 0,1,2,3,2,5,3
4)陣列第0號位置為0,0==0,遊標右移至第1號位置,1==1,繼續右移,直到第4號位置
5)陣列第4號位置為2, 2!=4, 2==array[2],說明2重複了,輸出2,演算法結束
思路三前提條件:
1)陣列中數字範圍為[0,n-1]
2)陣列長度為n
三、演算法程式碼
【題型一】n個正整數 -- [1,n] 找重複
#include <stdio.h>
#include <stdlib.h>
/*******************************
Author:tmw
date:2018-3-17
********************************/
int* mark_times_appear(int* array, int len)
{
if(array==NULL || len<0) return NULL;
int index = 0;
int i = 0;
while(i<len)
{
/**當前位為正數有效位,則執行交換**/
while( array[i] > 0 )
{
index = array[i];
/**待交換的位置上的元素是有效值的情況:
交換,並將當前待交換的元素下的值改成負數計數值
**/
if(array[index-1]>0) /**陣列下標從0開始**/
{
array[i] = array[index-1];
array[index-1] = -1;
}
/**待交換的位置上的元素是負數的情況:
說明該元素出現過一次了,負數計數需要減減來更新記錄
**/
else/**若需要返回出現兩次的元素,可以直接在這裡做返回**/
{
//return array[i];
array[i] = 0;
array[index-1]--;
}
}
/**當前位為負數,緊接著判斷下一位**/
i++;
}
return array;
}
【題型二】n個數[0,n] 找重複
/**************************************************
author:tmw
date:2018-8-22
**************************************************/
#include <stdio.h>
#include <stdlib.h>
#define swap(a,b,t) (t=a,a=b,b=t)
/** findDuplicatedNumber 找到任意一個重複的元素並輸出
* @param int* array -- 存數字的陣列
* @param int array_len -- 陣列長度
**/
int findDuplicatedNumber( int* array, int array_len )
{
/**入參判斷**/
if( array == NULL || array_len <= 0 )
return -1;
int i;
/**這一步可省略:驗證陣列內的元素滿足給定條件:元素取值[0,n-1],長度為n**/
for( i=0; i<array_len; i++ )
{
if( array[i] < 0 || array[i] > array_len-1 )
return -1;
}
/**主演算法**/
int temp=0;
for( i=0; i<array_len; i++ )
{
while( array[i] != i )
{
if( array[i] != array[array[i]] )
swap(array[i], array[array[i]], temp);
else
return array[i];
}
}
return -1; //沒找著,返回-1
}
四、測試程式碼及結果
【題型一】的測試結果
int main()
{
printf("測試程式碼!\n");
int* array;
int i;
array = (int*)malloc(5*sizeof(int));
printf("請輸入5個數組元素:\n");
for( i=0;i<5;i++ )
scanf("%d",&array[i]);
array = mark_times_apear(array,5);
for( i=0;i<5;i++ )
printf("%d ",array[i]);
return 0;
}
夢想還是要有的,萬一實現了呢~~~ヾ(◍°∇°◍)ノ゙~~~