Skip to content

Commit

Permalink
update: dsa 117.md
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnynode committed Oct 4, 2023
1 parent 856914c commit da6b720
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 3 deletions.
3 changes: 2 additions & 1 deletion dsa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,5 @@ Data Structures and Algorithms (DSA)
- [113、数据结构与算法之堆: Leetcode 215. 数组中的第K个最大元素 (Typescript版)](./contents/113.md)
- [114、数据结构与算法之堆: Leetcode 347. 前K个高频元素 (Typescript版)](./contents/114.md)
- [115、数据结构与算法之堆: Leetcode 23. 合并 K 个升序链表 (Typescript版)](./contents/115.md)
- [116、数据结构与算法之堆: 实现最大堆类的两种方式及实现堆排序 (Typescript版)](./contents/116.md)
- [116、数据结构与算法之堆: 实现最大堆类的两种方式及实现堆排序 (Typescript版)](./contents/116.md)
- [117、数据结构与算法之堆: Leetcode 451. 根据字符出现频率排序 (Typescript版)](./contents/117.md)
4 changes: 2 additions & 2 deletions dsa/contents/116.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ class MaxHeap {
// 构建最大堆的过程,递归
static maxHeapify (Arr, i, size) {
// 左节点(索引)
let l = (i << 1) + 1
const l = (i << 1) + 1;
// 右节点
let r = (i << 1) + 2
const r = (i << 1) + 2;
let largest = i;
// 父节点i和左节点l做比较取最大
if (l <= size && Arr[l] < Arr[largest]) largest = l;
Expand Down
147 changes: 147 additions & 0 deletions dsa/contents/117.md
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)

0 comments on commit da6b720

Please sign in to comment.