題意即找出陣列中超過一半的數。
最直接的方法是對所有id排序,然後再掃瞄一遍排好的id列表,統計各個id出現的次數,若某個id次數超過總數的一半則輸出這個id,這個演算法的時間複雜度為o(n * log2n + n)。
但事實上,若id列表有序,不必再次掃瞄列表。若乙個id出現的次數超過總數n的一半,那麼這個有序的id列表中的第n/2項(從0開始編號)一定會是這個id。這樣能迅速定位到列表的某一項的話(比如用陣列),該時間複雜度為o(n * log2n )。
但上面兩種方法都要先對id列表進行排序,時間複雜度方面沒有本質的改進,能否避免排序呢?
如果每次刪除 2 個不同的id(不管是否包含「水王」的id),那麼在剩下的id列表中,「水王」的id出現的次數仍然超過總數的一半。可以通過不斷重複這個過程把id列表中的id總數降低**化為更小的問題),從而得到答案。
這個思路,避免了排序這個耗時的步驟,總的時間複雜度只有o(n),且只需要常數的額外記憶體。
1 type find(type *id, intn)8view codeelse14}
15return
candidate;
16 }
這個題目體現了電腦科學中很普通的思想,就是如何把乙個問題轉化為規模較小的若干個問題。分治、遞推和貪心等都是基於這樣的思路。在轉化的過程中,小的問題跟原問題本質上一致。同樣我們可以將小問題轉化為更小的問題,因此轉化過程是很重要的。像上面這題,我們保證了問題的解在小問題中仍然具有與原問題相同的性質:水王的id在id列表中的數量超過一半。轉化的效率越高,轉化之後問題規模縮小得越快,則整體的時間複雜度越低。
擴充套件問題:
現在有3個發帖很多的id,他們的發帖數目都超過了帖子總數目n的1/4,你能從發帖id列表中快速找出他們的id嗎?
思路與本題一樣用抵消法,每次刪除 4 個不同的id即可。
1 type candidate[3];2view codevoid find1(type *id, int
n) ;
4for(int i = 0; i < n; ++i) else
if(ntimes[1] == 0 && id[i] != candidate[0
]) else
if(ntimes[2] == 0 && id[i] != candidate[0] && id[i] != candidate[1
]) else26}
27}28 }
程式設計之美 2 3 尋找發帖「水王」
題目 尋找乙個id列表中,有乙個id超過了總數的一半,找出這個id 分析 可以對id進行排序,因為需要尋找的id超過了一半,所以該id列表id n 中,id n 2 一定是這個id值。複雜度為o nlgn 1 如果用排序這種方法解決的話,複雜度應該就維持在這個水平,不會降低。再思考一下,難道一定需要...
程式設計之美2 3 尋找發帖水王
這道題目由於不容易寫測試用例,所以,可以把題目轉換為 在乙個陣列中,有乙個數字出現的次數超過了陣列大小的一半,這和題目原意是一樣的。這道題目的思想是我們同時去掉陣列中兩個不一樣的數字,那麼,陣列中原來存在的規律是不變的 僅針對這個題目 好吧,還是先給出函式宣告 2.3 尋找發帖水王 bool dut...
程式設計之美2 3 尋找發帖「水王」
題目1 找到陣列中出現次數超過一半的元素 解法 使用另外不到1 2的元素與所求元素進行抵消 整個陣列抵消不同的兩個數,則超過1 2的元素還是原來的元素 public class main public static int findnum int input throws exception if ...