LeetCode 451. Sort Characters By Frequency

LeetCode 451. Sort Characters By Frequency

這題從題意上面就可以得知它是希望你算出頻率,基本上這也是很標準 Heap 的題型。

先來看一下這個問題原文長怎樣?


Given a string s, sort it in decreasing order based on the frequency of the characters. The frequency of a character is the number of times it appears in the string.

Return the sorted string. If there are multiple answers, return any of them.

Example 1:

Input: s = “tree”
Output: “eert”
Explanation: ‘e’ appears twice while ‘r’ and ‘t’ both appear once.
So ‘e’ must appear before both ‘r’ and ‘t’. Therefore “eetr” is also a valid answer.

Example 2:

Input: s = “cccaaa”
Output: “aaaccc”
Explanation: Both ‘c’ and ‘a’ appear three times, so both “cccaaa” and “aaaccc” are valid answers.
Note that “cacaca” is incorrect, as the same characters must be together.

Example 3:

Input: s = “Aabb”
Output: “bbAa”
Explanation: “bbaA” is also a valid answer, but “Aabb” is incorrect.
Note that ‘A’ and ‘a’ are treated as two different characters.

Constraints:

  • 1<=s.length<=51051 <= s.length <= 5 * 10^5
  • s consists of uppercase and lowercase English letters and digits.

給定你一個字串,需要你回傳一個字串,依照出現的頻率來排列,也就是說當你字元的頻率出現的越高,則位置越前面,舉個例子來說,假設 "abca" 則會輸出 "aabc",此時你會好奇,那 'b''c' 頻率一樣,誰先誰後,照題目來看,其實他不在意,因此,任何一種順序都是合法的。

這邊要算次數,我們很直覺的就會想到使用 Map 來裝,所以在這樣的情況下,就會宣告一個 HashMap 來處理,首先我們將 Key 設定為字串內的字元, Value 則是裝字元出現的次數,所以我們可以這樣來進行宣告表示。

val hashMap = HashMap<Char, Int>()
for(i in s.indices){
    hashMap[s[i]] = hashMap.getOrDefault(s[i], 0)+1
}

接著我們可以宣告一個 MaxHeap 來幫我們排序已經儲存的好的 Map 元素,之前我們都只是使用內建的變數型態來當作 Heap 的多型處理,現在如果要裝入 Map 的 Key 與 Value 型態,那應該怎麼處理呢?很簡單,其實你可以透過 Map.Entry 物件或者 Pair 物件來進行存取,

val maxHeap = PriorityQueue<Map.Entry<Char,Int>>(){ e1, e2->
    e2.value - e1.value
}
//或者
val maxHeap = PriorityQueue<Pair<Char,Int>>(){ p1, p2->
    p2.second - e1.second
}

其中要注意的是,如果你使用 Pair 物件型態,則 Pair.first 代表 Key,而 Pair.second 代表的是 Value,還有另外一種表示方式就是自己宣告一個 class 裡面裝著 Key 與 Value 的物件也是可以的哦。
如果你是直接使用 Map.Entry 的方式處理,則裝進 Map 可以直接透過 Map.addAll() 的方式直接把 Map 倒入到 Heap 內。

maxHeap.addAll(hashMap.entries)

接著因為我們需要把結果 String 產出,所以我們透過了 StringBuilder 來協助我們裝結果字串。

val result = StringBuilder(s.length)

接著我們需要把 Heap 內部的物件全部吐出來,由於吐出來是我們的 Map.Entry 物件,因此,抓到 Key 之後,還要根據 Value 來決定產出幾次的字元。

while(maxHeap.isNotEmpty()){
    val entry = maxHeap.poll()
    for(i in 0..entry.value-1){
        result.append(entry.key)
    }
}

如此一來就可以把最終結果輸出了。

return result.toString()

以下就是完整程式碼。

class Solution {
    fun frequencySort(s: String): String {
        val hashMap = HashMap<Char, Int>()
        val size = s.length - 1
        for(i in 0..size){
            hashMap[s[i]] = hashMap.getOrDefault(s[i], 0)+1
        }
        val maxHeap = PriorityQueue<Map.Entry<Char,Int>>(){e1,e2->
            e2.value - e1.value
        }
        maxHeap.addAll(hashMap.entries)
        val sortString = StringBuilder(s.length)
        while(maxHeap.isNotEmpty()){
            val entry = maxHeap.poll()
            for(i in 0..entry.value-1){
                sortString.append(entry.key)
            }
        }
        return sortString.toString()
    }
}

這邊可以看出我們的複雜度,首先把字串裝進 Map 就是 O(N),接著再把 Map 的值丟進 Heap,每次進去都會進行排序,所以整體就會是 O(NLogN),但是這邊注意到我們根據題目限制只會存在26個字母(大小寫)以及數字,所以充其量就只是 62 次,因此這邊的複雜度就會是 O(1),所以我們可以知道下方的迴圈都其實最多 62 次,都可以視為 O(1),因此,整體複雜度就會是一開始的計算次數 O(N)