1. 程式人生 > >Leetcode: SingleNumber I & II 陣列中只出現一次的數

Leetcode: SingleNumber I & II 陣列中只出現一次的數

     題目:I:Given a non-empty array of integers, every element appears two times except for one, which appears exactly once. Find that single one.
       Note:  Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
       在一個數組中,除了一個數字出現一次,其他都出現兩次。請找出這個數
      注意:你的演算法應該是一個線性複雜度。

     這個題目就是讓你在O(n)的時間複雜度下,找出那個只出現一次的數。對於沒有接觸過這類題的人,乍一看無從下手。其實如果你知道了位操作的話這題也就不難解了: 在位運算中,異或操作符能夠很好地將兩個數“抵消”    因為相同的兩個數,它們每位的值都是相等的。我們知道相同的位(0,1)異或的結果為0 ,這樣兩個相同的數(這裡可以擴充套件到偶數個)異或結果為0,而任意數與0異或結果都為本身。有了這個思路,這個演算法也就很簡單了。

//* 出現兩次 找到只出現一個的那個數

int SingleNumber_d(int[] a,int len)
{
	int res = 0;
	for(int i=0;i<len;++i)
	{
		res=res^a[i];
	}
	return res;
}

是不是很簡單呢,其實 推廣到所有偶數個數,都可以實現,這就是異或的巧妙運算。

進階版:II:Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

給出一個數組,除了一個數出現一次,其他都出現3次,請找出這個數。時間複雜度線性範圍內。

前面的偶數個數可以用異或來實現。那麼奇數個數的又如何來實現呢?

     一種是考慮二進位制的方式,利用“位相加”的方法,對於int型的數,我們可以定義一個bits[32]的陣列,在二進位制層面上,按位進行相加,如果陣列的數都出現3次的話,那麼對於每一位相加的和%3必然等於0,現在多了個只出現一次的數,對3取餘結果不為0 的那位,必然是隻出現一次的數在此位上為1。這樣我們就能計算出這個數了。

大家可以對照著code理解:

//*  記錄32位的各位位數和,%3,

int SingleNumber_t1(int[] a,int len)
{
	int bits[32]={0};  // 用於記錄每位的和
	
	for(int i=0;i<len;++i)
	{
		for(int j=0;j<32;++j)
		{
			bit[j] += (a[i]>>j)&1   // 位操作,記錄求和
		}
	}
	// 對求和結果取餘,不為0的則為只出現一次的。最後轉為10進位制結果。
	int res=0;
	for(int j=0;j<32;j++)
	{
		if(bits[j]%3 != 0)
			res += 1<<j;
	}
	return res;
}

核心思路就是以位的層面上進行操作,推廣一下的話,其實出現任意N次,都可以用這種方法來求解。

第二種思路就比較厲害了,是Leetcode上的一位大神寫的,原理上還是利用“抵消”的思想,出現兩次一個異或就能解決,出現三次的就需要更加複雜的操作了(0,1,2)。整體解決思路就是 跑三遍然後能“抵消”。

// *進階版
// *出現三次  找到只出現一個的那個數
int SingleNumber_t2(int[] a,int len)
{
	int a=0,b=0;
	for(int i=0;i<len;++i)
	{
		a = (a^a[i]) & ~b;
		b = (b^a[i]) & ~a;      // * what the fuck?
	}
	return a;
}

是不是看的很懵逼,只能說能想到這種寫法的人,對於位操作的理解是相當的深入了。