王道/天勤 課後習題 尋找數組的主元素 詳解,絕對能看懂!

主元素就是指數組中,出現次數大於數組長度的一半的數。
在習題中,給出的解答如下:

int majority(int A[],int n)
{
	int i, c, count = 1;
	c = A[0];
	for(i=1;i<n;i++)
	{
		if (A[i] == c)
			count++;
		else
		{
			if (count > 0)
				count--;
			else
			{
				c = A[i];
				count = 1;
			}
		}
	}
	if(count>0)
	{
		for(i=count=0;i<n;i++)
		{
			if (A[i] == c)
				count++;	
		}
	}
	if (count > n / 2) return c;
	return -1;
}

具體思路是:
1.篩選可能的主元素,記錄爲a(只篩選一次)
2.判斷該原元素是否爲主元素。(遍歷一次,記錄次數,看是否大於長度的一半)

這裏最大的問題在於:
爲什麼只需要篩選一次,就可以得到候選主元素?

下面將從數學的角度去說明這一點。

前提:
1.假設主元素出現了x次,那這個數組的長度最長是2*x-1.也就是說,主元素出現次數至少大於數組長度的一半。
推論:
一、記錄第一個元素,標記次數count=1.
下一次遇到該元素則+1,沒有遇到則-1.
如果這個元素是主元素,則最終count必然大於1.

下面我們從最簡單的例子來入手:

1.假設這個數組爲A={1,1}則顯然按照上面的計算,count=2>0,爲主元素。
2.如果A={1,2}呢?count=1-1=0,不大於0,所以不是主元素

那我們遞推一下。

1.如果A={1,1,2},則count=1+1-1=1>0,成立
2.如果A={1,2,1},則對最初的1來講,剛開始就遇到了2,於是count(1)=1-1=0。

接下來就是最重要的部分了!!!
標準答案在處理的時候,跳過了數組中count=0的第一位(1),繼續處理之後的第二位(2),並且把count(2)=1

繼續按照之前的算法,數組的第二位(2)和第三位(1)進行比較,發現不同,因此count(2)=1-1=0
接下來繼續循環,來判斷第三位(1),並將count(3)置爲1.

此時循環已經結束了。
最後留下的count=1的數據,正是1.
因此只需要判斷1是不是主元素即可。

最後檢驗1的確是主元素。

那麼這是不是巧合呢?爲什麼可以不管前面的那些count=0的數據呢?

結論1
凡是出現了count=0,則表示該元素從計數起到結束,所經歷的數組元素中,有一半是該元素,有一半不是。

例如11122211222
對於開始的1,結束的時候是在111222的位置。這期間1的出現次數,和“非1”的出現次數一樣。

結論2
如果經歷了一段數組,其中有一半是元素a,另一半不是a,則可以去掉這經歷的一半,因爲這裏面的即使有主元素,根據假設1的對稱性,也可以去掉這一段數組,從剩餘數組中尋找候選主元素

這樣看來,這個算法是正確的。

有一種極端情況,例如121212
最終前五個數的count均爲0,只有最後一個2,因爲沒有後續,所以count=1,此時會只需要判斷一下,此時2就是候選主元素。只需要遍歷一次數組,看看2是否爲主元素即可。

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