Skip to content

Commit add5058

Browse files
committed
✨ feat: refactor huffman
1 parent e18c3f3 commit add5058

File tree

8 files changed

+66
-58
lines changed

8 files changed

+66
-58
lines changed

Diff for: MIGRATION.md

+7
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,10 @@ No breaking changes.
159159
### @algorithm.ts/graph
160160

161161
1. The graph related types is moved to `@algorithm.ts/types`.
162+
163+
164+
### @algorithm.ts/huffman
165+
166+
1. `buildEncodingTable` is renamed to `toEncodingTable`.
167+
2. `buildHuffmanTree` is renamed to `fromEncodingTable`.
168+
3. `createHuffmanTree` is renamed to `fromText`.

Diff for: packages/huffman/README.md

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<header>
22
<h1 align="center">
3-
<a href="https://github.com/guanghechen/algorithm.ts/tree/release-2.x.x/packages/huffman#readme">@algorithm.ts/huffman</a>
3+
<a href="https://github.com/guanghechen/algorithm.ts/tree/release-3.x.x/packages/huffman#readme">@algorithm.ts/huffman</a>
44
</h1>
55
<div align="center">
66
<a href="https://www.npmjs.com/package/@algorithm.ts/huffman">
@@ -74,19 +74,18 @@ A typescript implementation of the **huffman** coding.
7474

7575
```typescript
7676
import { encode } from '@algorithm.ts/huffman'
77-
7877
const { encodedData, encodingTable, tree } = encode('Hello, world!')
7978
```
8079

8180
* `decode`: Decode a huffman encoded data to string.
8281

8382
```typescript
84-
import { decode } from '@algorithm.ts/huffman'
83+
import { decode, fromEncodingTable } from '@algorithm.ts/huffman'
8584

8685
const plaintext = decode(encodedData, tree)
8786

8887
// Or build tree from encodingTable
89-
const tree2 = buildHuffmanTree(encodingTable)
88+
const tree2 = fromEncodingTable(encodingTable)
9089
const plaintext2 = decode(encodedData, tree2)
9190
```
9291

@@ -144,7 +143,7 @@ A typescript implementation of the **huffman** coding.
144143
const encodingTable = decompressEncodingTable(
145144
JSON.parse(textDecoder.decode(base64.decode(encodingTableText))),
146145
)
147-
const tree = huffman.buildHuffmanTree(encodingTable)
146+
const tree = huffman.fromEncodingTable(encodingTable)
148147

149148
const cipherData = huffman.decompress(base64.decode(cipherText))
150149
const plaintext = huffman.decode(cipherData, tree)
@@ -181,4 +180,4 @@ A typescript implementation of the **huffman** coding.
181180
* [Huffman coding | Wikipedia](https://en.wikipedia.org/wiki/Huffman_coding)
182181

183182

184-
[homepage]: https://github.com/guanghechen/algorithm.ts/tree/release-2.x.x/packages/huffman#readme
183+
[homepage]: https://github.com/guanghechen/algorithm.ts/tree/release-3.x.x/packages/huffman#readme

Diff for: packages/huffman/__test__/huffman.spec.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { buildHuffmanTree, compress, decode, decompress, encode } from '../src'
1+
import { compress, decode, decompress, encode, fromEncodingTable } from '../src'
22

33
describe('basic', function () {
44
test('empty', () => textWrapper(''))
@@ -18,7 +18,7 @@ describe('basic', function () {
1818
const text2: string = decode(encodedData, tree)
1919
expect(text2).toEqual(text)
2020

21-
const tree2 = buildHuffmanTree(encodingTable)
21+
const tree2 = fromEncodingTable(encodingTable)
2222
expect(tree).toEqual(tree2)
2323

2424
expect(decode(encodedData, tree2)).toEqual(text)
@@ -32,9 +32,8 @@ describe('basic', function () {
3232

3333
test('unexpected', function () {
3434
const { encodedData, encodingTable } = encode('Hello, world!')
35-
3635
const { H, ...encodingTable2 } = encodingTable
37-
const tree2 = buildHuffmanTree(encodingTable2)
36+
const tree2 = fromEncodingTable(encodingTable2)
3837

3938
expect(() => decode(encodedData, tree2)).toThrow(/Bad encoded data or huffman tree/)
4039
})

Diff for: packages/huffman/__test__/uri-compress.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function decompress(compressedText: string): string {
5050
const encodingTable = decompressEncodingTable(
5151
JSON.parse(textDecoder.decode(base64.decode(encodingTableText))),
5252
)
53-
const tree = huffman.buildHuffmanTree(encodingTable)
53+
const tree = huffman.fromEncodingTable(encodingTable)
5454

5555
const cipherData = huffman.decompress(base64.decode(cipherText))
5656
const plaintext = huffman.decode(cipherData, tree)

Diff for: packages/huffman/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
},
99
"repository": {
1010
"type": "git",
11-
"url": "https://github.com/guanghechen/algorithm.ts/tree/release-2.x.x",
11+
"url": "https://github.com/guanghechen/algorithm.ts/tree/release-3.x.x",
1212
"directory": "packages/huffman"
1313
},
14-
"homepage": "https://github.com/guanghechen/algorithm.ts/tree/release-2.x.x/packages/huffman#readme",
14+
"homepage": "https://github.com/guanghechen/algorithm.ts/tree/release-3.x.x/packages/huffman#readme",
1515
"keywords": [
1616
"algorithm",
1717
"huffman",

Diff for: packages/huffman/src/encode.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { buildEncodingTable, createHuffmanTree } from './huffman'
1+
import { fromText, toEncodingTable } from './huffman'
22
import type { IHuffmanEncodedData, IHuffmanEncodingTable, IHuffmanNode } from './huffman'
33

44
/**
@@ -11,9 +11,9 @@ export function encode(plaintext: string): {
1111
encodingTable: IHuffmanEncodingTable
1212
tree: IHuffmanNode
1313
} {
14-
const tree: IHuffmanNode = createHuffmanTree(plaintext)
14+
const tree: IHuffmanNode = fromText(plaintext)
1515
const encodedData: Array<0 | 1> = []
16-
const encodingTable: IHuffmanEncodingTable = buildEncodingTable(tree)
16+
const encodingTable: IHuffmanEncodingTable = toEncodingTable(tree)
1717
for (const c of plaintext) {
1818
// Invariant: data should not be null / undefined.
1919
const data: IHuffmanEncodedData = encodingTable[c]

Diff for: packages/huffman/src/huffman.ts

+41-38
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createPriorityQueue } from '@algorithm.ts/priority-queue'
1+
import { PriorityQueue } from '@algorithm.ts/queue'
22

33
export interface IHuffmanNode {
44
value?: string
@@ -11,9 +11,11 @@ export type IHuffmanEncodedData = Array<0 | 1>
1111
export type IHuffmanEncodingTable = Record<string, IHuffmanEncodedData>
1212

1313
/**
14-
* Create a Huffman tree for the specified texts.
14+
* Create a Huffman tree from the specified text.
15+
* @param text
16+
* @returns
1517
*/
16-
export function createHuffmanTree(text: string): IHuffmanNode {
18+
export function fromText(text: string): IHuffmanNode {
1719
const costMap: Record<string, number> = {}
1820
for (const c of text) {
1921
const cnt: number = costMap[c] ?? 0
@@ -23,55 +25,30 @@ export function createHuffmanTree(text: string): IHuffmanNode {
2325
const entries = Object.entries(costMap)
2426
if (entries.length <= 0) return {}
2527

26-
const priorityQueue = createPriorityQueue<{
28+
const minHeap = new PriorityQueue<{
2729
cost: number
2830
node: IHuffmanNode
29-
}>((x, y) => y.cost - x.cost)
30-
priorityQueue.init(entries.map(([value, cost]) => ({ cost, node: { value } })))
31-
while (priorityQueue.size() > 1) {
32-
const o1 = priorityQueue.dequeue()!
33-
const o2 = priorityQueue.dequeue()!
31+
}>({ compare: (x, y) => x.cost - y.cost })
32+
minHeap.init(entries.map(([value, cost]) => ({ cost, node: { value } })))
33+
34+
while (minHeap.size > 1) {
35+
const o1 = minHeap.dequeue()!
36+
const o2 = minHeap.dequeue()!
3437
const o: IHuffmanNode = { left: o1.node, right: o2.node }
35-
priorityQueue.enqueue({
38+
minHeap.enqueue({
3639
cost: o1.cost + o2.cost,
3740
node: o,
3841
})
3942
}
40-
return priorityQueue.top()!.node
41-
}
42-
43-
/**
44-
* Build a HuffmanEncodingTable based on Huffman tree.
45-
* @param tree
46-
* @returns
47-
*/
48-
export function buildEncodingTable(tree: IHuffmanNode): IHuffmanEncodingTable {
49-
const prefix: IHuffmanEncodedData = []
50-
const encodingTable: IHuffmanEncodingTable = {}
51-
collect(tree, 0)
52-
return encodingTable
53-
54-
function collect(node: IHuffmanNode, cur: number): void {
55-
if (node.value) encodingTable[node.value] = prefix.slice(0, cur)
56-
else {
57-
if (node.left) {
58-
prefix[cur] = 0
59-
collect(node.left, cur + 1)
60-
}
61-
if (node.right) {
62-
prefix[cur] = 1
63-
collect(node.right, cur + 1)
64-
}
65-
}
66-
}
43+
return minHeap.front()!.node
6744
}
6845

6946
/**
7047
* Build a huffman tree based on HuffmanEncodingTable.
7148
* @param table
7249
* @returns
7350
*/
74-
export function buildHuffmanTree(table: IHuffmanEncodingTable): IHuffmanNode {
51+
export function fromEncodingTable(table: IHuffmanEncodingTable): IHuffmanNode {
7552
const entries = Object.entries(table)
7653
if (entries.length === 0) return {}
7754

@@ -91,3 +68,29 @@ export function buildHuffmanTree(table: IHuffmanEncodingTable): IHuffmanNode {
9168
}
9269
return root
9370
}
71+
72+
/**
73+
* Build a HuffmanEncodingTable based on Huffman tree.
74+
* @param tree
75+
* @returns
76+
*/
77+
export function toEncodingTable(tree: IHuffmanNode): IHuffmanEncodingTable {
78+
const prefix: IHuffmanEncodedData = []
79+
const encodingTable: IHuffmanEncodingTable = {}
80+
collect(tree, 0)
81+
return encodingTable
82+
83+
function collect(node: IHuffmanNode, cur: number): void {
84+
if (node.value) encodingTable[node.value] = prefix.slice(0, cur)
85+
else {
86+
if (node.left) {
87+
prefix[cur] = 0
88+
collect(node.left, cur + 1)
89+
}
90+
if (node.right) {
91+
prefix[cur] = 1
92+
collect(node.right, cur + 1)
93+
}
94+
}
95+
}
96+
}

Diff for: packages/huffman/src/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { compress, decompress } from './compress'
22
import { decode, encode } from './encode'
3-
import { buildEncodingTable, buildHuffmanTree, createHuffmanTree } from './huffman'
3+
import { fromEncodingTable, fromText, toEncodingTable } from './huffman'
44

55
export * from './compress'
66
export * from './encode'
@@ -11,7 +11,7 @@ export default {
1111
decode,
1212
compress,
1313
decompress,
14-
buildEncodingTable,
15-
createHuffmanTree,
16-
buildHuffmanTree,
14+
fromText,
15+
fromEncodingTable,
16+
toEncodingTable,
1717
}

0 commit comments

Comments
 (0)