問題是:給你一個數組,求解出現次數第K多的元素。當然leetcode上的要求是算法復雜度不能大於O(N*logN)。
首先這個問題我先是在leetcode上看到,當時想了兩種做法,做到一半都覺得不是很好,正在思考別的方法。然后在牛客網上看別人的面試經歷,看到一個應聘者和用我幾乎完全一樣的思路嘗試在面試中解決這個問題(HashMap-->TreeSet),但是都沒解決出來。這個問題確實是一個乍看不難但是要實際解決又會不停發現自己思路有問題的問題,於是我索性記錄一下這兩種想法和解決之道。
拿到這個問題的時候我第一時間想到了用HashMap解決,鍵值對的鍵是數組中的元素的值,值是目前出現的次數,當第一次出現的時候值給1,以后發現已經存在那么就取出值加1再放進去。這個方法的思想是用類似於上一篇博文中的方法來做。核心是用hash算法形成一個元素與儲存位置的映射來實現查找操作的常量時間復雜度。這么做的結果是你能在O(N)的算法復雜度情況下得到一個HashMap,它儲存了每一種元素和其出現的個數。這個時候一個很具體的問題就出現了,下一步本來需要對出現次數進行一個排序,然后取出出現次數第K大的那個元素。但是問題是鍵值對的映射方式是從鍵映射到值,也就是說你用HashMap可以找出值的集合,但是無法找出與這個值對於的鍵。比如你通過排序知道了最頻繁的次數是5,但是你不知道到底誰出現了5次。這個地方其實我要解釋一下,不是說你就完全做不到實現反映射,而是第一,HashMap實現的是映射你強行根據值尋找鍵不符合其設計初衷,第二,實現的過程比較復雜我想需要很大的算法復雜度完全抵消了使用HashMap帶來的好處。比如一個鍵只能對應一個值,但是一個值可能有多個鍵對應。(每一個數組中的元素都有確定的出現次數,但是出現次數為K的元素可能不止一個)。要解決這些問題等等。真的解決了也就不是這道題目的問題了。
第二時間我想到了用TreeSet,因為它既可以根據你的重寫的equal()方法判斷是否等於和去重,又可以對你輸入數據用comparator進行排序。我就想,我干脆寫一個類,里面即方數字本身用於比較是否相等(hashcode和equal),又放當前出現的次數,又實現comparator接口來對當前出現的次數進行排序。我的期望是好的,但是在一開始的思考的時候我忽視了(確實沒想到)一個根本的問題,這個問題直接導致我的這個想法完全不能按照我設想的實現。隨着你遍歷的進行,你的某個數的出現的次數可能從1變到2變到3。所以你有一個不停動態修改TreeSet的內容對象的過程。但是你無法獲得TreeSet中值為當前數組值的那個對象的引用。比如說我現在遍歷到數組中的5這個元素,於是我去TreeSet中找值為5的對象,但是找不到(沒有提供相應的方法)。當然確實也不應該有這樣的方法,你想你都知道值是5了,可是你又不知道具體引用的值,TreeSet也沒辦法幫你。如果用遍歷強行去找會導致一個很復雜的過程,算法復雜度高(紅黑樹的遍歷)。同時,TreeSet是按照某種規律排序的,大量的修改其值的操作(通過先刪除再add,或者這個問題中的用add覆蓋)會影響其效率因為要維護某種順序。所以這個問題中也不推薦這么做。
雖然以上兩種做法都么能直接解決這個問題,但是給我帶來了思考。
最后還是做出來了,大致就是第一種方法不過鍵值對的值是我寫的類,最后再用堆排序。這個做法還是挺蠢得我覺得。貌似最佳做法使用桶排序O(N)?以后再研究研究這個。