給定數組A,大小爲n,數組元素爲1到n的數字,統計哪些數字沒有出現,哪些數字出現了多少次


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);
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章