-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
856914c
commit da6b720
Showing
3 changed files
with
151 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
### 根据字符出现频率排序 | ||
|
||
- https://leetcode.cn/problems/sort-characters-by-frequency/ | ||
|
||
### 描述 | ||
|
||
- 给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。 | ||
- 返回 已排序的字符串 。如果有多个答案,返回其中任何一个。 | ||
|
||
### 示例 1 | ||
|
||
``` | ||
输入: s = "tree" | ||
输出: "eert" | ||
解释: 'e'出现两次,'r'和't'都只出现一次。 | ||
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。 | ||
``` | ||
|
||
### 示例 2 | ||
|
||
``` | ||
输入: s = "cccaaa" | ||
输出: "cccaaa" | ||
解释: 'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。 | ||
注意"cacaca"是不正确的,因为相同的字母必须放在一起。 | ||
``` | ||
|
||
### 示例 3 | ||
|
||
``` | ||
输入: s = "Aabb" | ||
输出: "bbAa" | ||
解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。 | ||
注意'A'和'a'被认为是两种不同的字符。 | ||
``` | ||
|
||
### 提示 | ||
|
||
- 1 <= s.length <= 5 * $10^5$ | ||
- s 由大小写英文字母和数字组成 | ||
|
||
### 算法实现 | ||
|
||
1 )普通方法实现, 基于原生sort和Map结构 | ||
|
||
```ts | ||
function frequencySort(s: string): string { | ||
// 1. 构建map字典,例如: Map{a: 2, b: 3} | ||
const map = new Map(); | ||
s.split('').forEach(item => { | ||
map.set(item, map.has(item) ? map.get(item) + 1 : 1); | ||
}); | ||
// 2. 将map转成二维数组进行排序 | ||
const arr = Array.from(map); | ||
arr.sort((a, b) => b[1] - a[1]); | ||
// 3. 基于排好序的数组(降序)组装成最终结果 | ||
let result = ''; | ||
arr.forEach((item) => { | ||
result += item[0].repeat(item[1]); | ||
}) | ||
return result; | ||
}; | ||
``` | ||
|
||
- 这里使用平时最简单的原生排序法,结合Map数据结构的特性,和ES6中字符串的特性完成 | ||
- 原生排序,性能不错,推荐 | ||
|
||
2 )使用堆排序 | ||
|
||
```ts | ||
class MaxHeap { | ||
map: Map<string, number> = new Map() | ||
heap: number[] = [] | ||
init(str:string) { | ||
// 构建map字典 | ||
const { map } = this; | ||
str.split('').forEach(item => { | ||
map.set(item, map.has(item) ? map.get(item) + 1 : 1); | ||
}); | ||
this.heap = Array.from(map.values()); | ||
} | ||
sort () { | ||
let iArr = this.heap; | ||
let n = iArr.length; | ||
if (n <= 1) return iArr; | ||
for (let i = Math.floor(n / 2); i >= 0; i--) { | ||
MaxHeap.maxHeapify(iArr, i, n); | ||
} | ||
for (let j = 0; j < n; j++) { | ||
MaxHeap.swap(iArr, 0, n - 1 - j); | ||
MaxHeap.maxHeapify(iArr, 0, n - 1 - j - 1); | ||
} | ||
return iArr; | ||
} | ||
// 排序并转成字符串 | ||
sortToString () { | ||
const arr = this.sort(); // 这里对值进行排序 | ||
const str = []; | ||
while (arr.length) { | ||
const top = arr.pop(); | ||
for (let [k, v] of this.map) { | ||
// 值和值匹配 | ||
if (v === top) { | ||
str.push(k.repeat(v)); | ||
this.map.delete(k); // 使用过的key防止重复匹配 这里记得删除 | ||
break | ||
} | ||
} | ||
} | ||
return str.join(''); | ||
} | ||
// 交换两个元素 | ||
static swap (arr, i, j) { | ||
if (i === j) return; | ||
[arr[i], arr[j]] = [arr[j], arr[i]]; | ||
} | ||
// 构建最大堆的过程 | ||
static maxHeapify (Arr, i, size) { | ||
// 左节点(索引) | ||
const l = (i << 1) + 1; | ||
// 右节点 | ||
const r = (i << 1) + 2; | ||
let largest = i; | ||
// 父节点i和左节点l做比较取最大 | ||
if (l <= size && Arr[l] > Arr[largest]) largest = l; | ||
// 右节点和最大值比较 | ||
if (r <= size && Arr[r] > Arr[largest]) largest = r; | ||
if (largest !== i) { | ||
MaxHeap.swap(Arr, i, largest); | ||
MaxHeap.maxHeapify(Arr, largest, size); | ||
} | ||
} | ||
} | ||
|
||
function frequencySort(s: string): string { | ||
const mh = new MaxHeap(); | ||
mh.init(s); | ||
return mh.sortToString(); | ||
} | ||
``` | ||
|
||
- 如果这个堆之前构建好,只需要少许修改,即可投入使用 | ||
- 理解了最大堆的构建过程,这个还是比较推荐使用的 | ||
- 需要注意的是在while和for的嵌套循环中的时间复杂度的考量 | ||
* while是每次pop直到为0,因此是 n | ||
* for不会每次都执行n次,匹配到会被break掉,因此是 logn | ||
* 所以整体时间复杂度为 O(nlogn) |