1. 給定數組A,大小爲n,數組元素爲1到n的數字,不過有的數字出現了多次,有的數字沒有出現。請給出算法和程序,統計哪些數字沒有出現,哪些數字出現了多少次。能夠在O(n)的時間複雜度,O(1)的空間複雜度要求下完成麼?
分析:如果對時間複雜度沒有要求,那麼先排序再統計就ok,時間O(n*lgn),空間O(1)。如果對空間沒有要求,那麼使用位圖或者哈希表,時間O(1),空間O(n)。但是題目對兩個都有要求,那麼只能是在原數組進行統計才能滿足O(1)的空間複雜度。
首先想到的是類似計數排序的方法,問題的關鍵在於區分計數值和原來的值。比如,A[0]=5,那麼A[4]存儲5的計數應該加1,但是A[4]此時還存儲有其它值,該怎麼辦?
有兩種方法,第一種非常巧妙,實在是歎爲觀止!第二種使用另一種方法區分計數和原值。思路都是一樣的。
public class StatisArray {
//給定數組A,大小爲n,數組元素爲1到n的數字,統計哪些數字沒有出現,哪些數字出現了多少次
public static void statis1(int[] A){
if(A==null || A.length==0) return;
int n = A.length;
for(int i=0; i<n; i++) //使每個元素對n求餘得到其位置,同時避免A[0]==n造成的錯誤
A[i]--;
for(int i=0; i<n; i++){ //使用+n來計數,對n求餘得到原值,除以n得到計數
A[A[i]%n] += n;
}
for(int i=0; i<n; i++){
System.out.println((i+1)+" occurs "+(A[i]/n)+" times.");
}
/*for(int i=0; i<n; i++){ //使用+2n來計數,對n求餘得到原值,除以2n得到計數,避免A[0]==n造成的錯誤
A[A[i]%n] += n<<1;
}
for(int i=1; i<n; i++){
System.out.println(i+" occurs "+(A[i]/(n<<1))+" times.");
}
System.out.println(n+" occurs "+(A[0]/(n<<1))+" times.");*/
}
public static void statis2(int[] A){
if(A==null || A.length==0) return;
int n = A.length;
for(int i=0; i<n; i++){
if(A[i]>n) continue;
if(A[i] == i+1) {A[i] = n+1; continue;}
int t = A[i], j = t-1; //t要放到對應位置的值,j要放的位置
while(true){
if(j<=i) {if(A[j]>n) A[j]++; else A[j]=n+1; break;}//轉了一圈回到當前或之前的位置,break掉,防止錯誤累加
if(A[j]==t) A[j]=n+1; //對應位置原本就是自己,則計數
if(A[j]>n) {A[j]++; break;} //對應位置已經是計數,則自增並break
int tmp = A[j];
A[j] = n+1;
t = tmp;
j = t-1;
}
}
for(int i=0; i<n; i++)
System.out.println((i+1)+" occurs "+(A[i]>n ? A[i]-n : 0)+" times.");
}
public static void main(String[] args){
int A[] = {5,1,1,2,4};
//statis1(A);
statis2(A);
}
}