提交 d61d358f 编写于 作者: L luzhipeng

更新游程编码和huffman编码

上级 ab37a5ab
/*
* @lc app=leetcode id=139 lang=javascript
*
* [139] Word Break
*
* https://leetcode.com/problems/word-break/description/
*
* algorithms
* Medium (34.45%)
* Total Accepted: 317.8K
* Total Submissions: 913.9K
* Testcase Example: '"leetcode"\n["leet","code"]'
*
* Given a non-empty string s and a dictionary wordDict containing a list of
* non-empty words, determine if s can be segmented into a space-separated
* sequence of one or more dictionary words.
*
* Note:
*
*
* The same word in the dictionary may be reused multiple times in the
* segmentation.
* You may assume the dictionary does not contain duplicate words.
*
*
* Example 1:
*
*
* Input: s = "leetcode", wordDict = ["leet", "code"]
* Output: true
* Explanation: Return true because "leetcode" can be segmented as "leet
* code".
*
*
* Example 2:
*
*
* Input: s = "applepenapple", wordDict = ["apple", "pen"]
* Output: true
* Explanation: Return true because "applepenapple" can be segmented as "apple
* pen apple".
* Note that you are allowed to reuse a dictionary word.
*
*
* Example 3:
*
*
* Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
* Output: false
*
*
*/
/**
* @param {string} s
* @param {string[]} wordDict
* @return {boolean}
*/
var wordBreak = function(s, wordDict) {
};
/*
* @lc app=leetcode id=226 lang=javascript
*
* [226] Invert Binary Tree
*
* https://leetcode.com/problems/invert-binary-tree/description/
*
* algorithms
* Easy (57.14%)
* Total Accepted: 311K
* Total Submissions: 540.6K
* Testcase Example: '[4,2,7,1,3,6,9]'
*
* Invert a binary tree.
*
* Example:
*
* Input:
*
*
* ⁠ 4
* ⁠ / \
* ⁠ 2 7
* ⁠/ \ / \
* 1 3 6 9
*
* Output:
*
*
* ⁠ 4
* ⁠ / \
* ⁠ 7 2
* ⁠/ \ / \
* 9 6 3 1
*
* Trivia:
* This problem was inspired by this original tweet by Max Howell:
*
* Google: 90% of our engineers use the software you wrote (Homebrew), but you
* can’t invert a binary tree on a whiteboard so f*** off.
*
*/
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var invertTree = function(root) {
if (!root) return root;
// 递归
// const left = root.left;
// const right = root.right;
// root.right = invertTree(left);
// root.left = invertTree(right);
// 我们用stack来模拟递归
// 本质上递归是利用了执行栈,执行栈也是一种栈
// 其实这里使用队列也是一样的,因为这里顺序不重要
const stack = [root];
let current = null;
while ((current = stack.shift())) {
const left = current.left;
const right = current.right;
current.right = left;
current.left = right;
if (left) {
stack.push(left);
}
if (right) {
stack.push(right);
}
}
return root;
};
/*
* @lc app=leetcode id=231 lang=javascript
*
* [231] Power of Two
*
* https://leetcode.com/problems/power-of-two/description/
*
* algorithms
* Easy (41.66%)
* Total Accepted: 218.3K
* Total Submissions: 523.1K
* Testcase Example: '1'
*
* Given an integer, write a function to determine if it is a power of two.
*
* Example 1:
*
*
* Input: 1
* Output: true
* Explanation: 2^0 = 1
*
*
* Example 2:
*
*
* Input: 16
* Output: true
* Explanation: 2^4 = 16
*
* Example 3:
*
*
* Input: 218
* Output: false
*
*/
/**
* @param {number} n
* @return {boolean}
*/
var isPowerOfTwo = function(n) {
if (n <= 0) return false;
while (n > 2) {
n = n / 2;
}
return Number.isInteger(n);
// return Number.isInteger(Math.log2(n));
};
/*
* @lc app=leetcode id=416 lang=javascript
*
* [416] Partition Equal Subset Sum
*
* https://leetcode.com/problems/partition-equal-subset-sum/description/
*
* algorithms
* Medium (39.97%)
* Total Accepted: 79.7K
* Total Submissions: 198.5K
* Testcase Example: '[1,5,11,5]'
*
* Given a non-empty array containing only positive integers, find if the array
* can be partitioned into two subsets such that the sum of elements in both
* subsets is equal.
*
* Note:
*
*
* Each of the array element will not exceed 100.
* The array size will not exceed 200.
*
*
*
*
* Example 1:
*
*
* Input: [1, 5, 11, 5]
*
* Output: true
*
* Explanation: The array can be partitioned as [1, 5, 5] and [11].
*
*
*
*
* Example 2:
*
*
* Input: [1, 2, 3, 5]
*
* Output: false
*
* Explanation: The array cannot be partitioned into equal sum subsets.
*
*
*
*
*/
/**
* @param {number[]} nums
* @return {boolean}
*/
var canPartition = function(nums) {
};
/*
* @lc app=leetcode id=494 lang=javascript
*
* [494] Target Sum
*
* https://leetcode.com/problems/target-sum/description/
*
* algorithms
* Medium (44.86%)
* Total Accepted: 89.3K
* Total Submissions: 198.5K
* Testcase Example: '[1,1,1,1,1]\n3'
*
*
* You are given a list of non-negative integers, a1, a2, ..., an, and a
* target, S. Now you have 2 symbols + and -. For each integer, you should
* choose one from + and - as its new symbol.
* ⁠
*
* Find out how many ways to assign symbols to make sum of integers equal to
* target S.
*
*
* Example 1:
*
* Input: nums is [1, 1, 1, 1, 1], S is 3.
* Output: 5
* Explanation:
*
* -1+1+1+1+1 = 3
* +1-1+1+1+1 = 3
* +1+1-1+1+1 = 3
* +1+1+1-1+1 = 3
* +1+1+1+1-1 = 3
*
* There are 5 ways to assign symbols to make the sum of nums be target 3.
*
*
*
* Note:
*
* The length of the given array is positive and will not exceed 20.
* The sum of elements in the given array will not exceed 1000.
* Your output answer is guaranteed to be fitted in a 32-bit integer.
*
*
*/
/**
* @param {number[]} nums
* @param {number} S
* @return {number}
*/
var findTargetSumWays = function(nums, S) {
};
/*
* @lc app=leetcode id=518 lang=javascript
*
* [518] Coin Change 2
*
* https://leetcode.com/problems/coin-change-2/description/
*
* algorithms
* Medium (41.57%)
* Total Accepted: 39.7K
* Total Submissions: 94.6K
* Testcase Example: '5\n[1,2,5]'
*
* You are given coins of different denominations and a total amount of money.
* Write a function to compute the number of combinations that make up that
* amount. You may assume that you have infinite number of each kind of
* coin.
*
*
*
*
*
*
* Example 1:
*
*
* Input: amount = 5, coins = [1, 2, 5]
* Output: 4
* Explanation: there are four ways to make up the amount:
* 5=5
* 5=2+2+1
* 5=2+1+1+1
* 5=1+1+1+1+1
*
*
* Example 2:
*
*
* Input: amount = 3, coins = [2]
* Output: 0
* Explanation: the amount of 3 cannot be made up just with coins of 2.
*
*
* Example 3:
*
*
* Input: amount = 10, coins = [10]
* Output: 1
*
*
*
*
* Note:
*
* You can assume that
*
*
* 0 <= amount <= 5000
* 1 <= coin <= 5000
* the number of coins is less than 500
* the answer is guaranteed to fit into signed 32-bit integer
*
*
*/
/**
* @param {number} amount
* @param {number[]} coins
* @return {number}
*/
var change = function(amount, coins) {
};
......@@ -76,6 +76,12 @@ TODO
[609.find-duplicate-file-in-system]
[哈夫曼编码和游程编码]
[React hooks]
[React fiber]
[一个非常简单,但是有效的算法 - 布隆过滤器]
> 从这个算法可以对 tradeoff 有更入的理解。
## 题目地址
https://leetcode.com/problems/rle-iterator/description/
## 题目描述
```
Write an iterator that iterates through a run-length encoded sequence.
The iterator is initialized by RLEIterator(int[] A), where A is a run-length encoding of some sequence. More specifically, for all even i, A[i] tells us the number of times that the non-negative integer value A[i+1] is repeated in the sequence.
The iterator supports one function: next(int n), which exhausts the next n elements (n >= 1) and returns the last element exhausted in this way. If there is no element left to exhaust, next returns -1 instead.
For example, we start with A = [3,8,0,9,2,5], which is a run-length encoding of the sequence [8,8,8,5,5]. This is because the sequence can be read as "three eights, zero nines, two fives".
Example 1:
Input: ["RLEIterator","next","next","next","next"], [[[3,8,0,9,2,5]],[2],[1],[1],[2]]
Output: [null,8,8,5,-1]
Explanation:
RLEIterator is initialized with RLEIterator([3,8,0,9,2,5]).
This maps to the sequence [8,8,8,5,5].
RLEIterator.next is then called 4 times:
.next(2) exhausts 2 terms of the sequence, returning 8. The remaining sequence is now [8, 5, 5].
.next(1) exhausts 1 term of the sequence, returning 8. The remaining sequence is now [5, 5].
.next(1) exhausts 1 term of the sequence, returning 5. The remaining sequence is now [5].
.next(2) exhausts 2 terms, returning -1. This is because the first term exhausted was 5,
but the second term did not exist. Since the last term exhausted does not exist, we return -1.
Note:
0 <= A.length <= 1000
A.length is an even integer.
0 <= A[i] <= 10^9
There are at most 1000 calls to RLEIterator.next(int n) per test case.
Each call to RLEIterator.next(int n) will have 1 <= n <= 10^9.
```
## 思路
这是一个游程编码的典型题目。
该算法分为两个部分,一个是初始化,一个是调用`next(n)`.
我们需要做的就是初始化的时候,记住这个A。 然后每次调用`next(n)`的时候只需要
判断n是否大于A[i](i从0开始)
- 如果大于A[i], 那就说明不够,我们移除数组前两项,更新n,重复1
- 如果小于A[i], 则说明够了,更新A[i]
这样做,我们每次都要更新A,还有一种做法就是不更新A,而是`伪更新`,即用一个变量记录,当前访问到的数组位置。
> 很多时候我们需要原始的,那么就必须这种放了,我的解法就是这种方法。
## 关键点解析
## 代码
```js
/*
* @lc app=leetcode id=900 lang=javascript
*
......@@ -70,15 +141,28 @@
* @param {number[]} A
*/
var RLEIterator = function(A) {
this.A = A;
this.current = 0;
};
/**
* @param {number} n
* @return {number}
*/
RLEIterator.prototype.next = function(n) {
const A = this.A;
while(this.current < A.length && A[this.current] < n){
n = n - A[this.current];
this.current += 2;
}
if(this.current >= A.length){
return -1;
}
A[this.current] = A[this.current] - n; // 更新Count
return A[this.current + 1]; // 返回element
};
/**
......@@ -86,4 +170,9 @@ RLEIterator.prototype.next = function(n) {
* var obj = new RLEIterator(A)
* var param_1 = obj.next(n)
*/
```
## 扩展阅读
[run-length encode and huffman encode](../thinkings/run-length encode and huffman encode.md)
# 基础的数据结构
这篇文章不是讲解数据结构的文章,而是结合现实的场景帮助大家`理解和复习`数据结构与算法,
如果你的数据结构基础很差,建议先去看一些基础教程,再转过来看。
本篇文章的定位是侧重于前端的,通过学习前端中实际场景的数据结构,从而加深大家对数据结构的理解和认识。
## 线性结构
### 数组
......
## 游程编码和哈夫曼编码
### huffman encode(哈夫曼编码)
huffman 编码的基本思想就是用短的编码表示长的字符序列,用长的编码来表示短的字符序列,从而实现压缩的目的。
因此huffman编码被广泛地应用于无损压缩领域。
上面提到了他的基本原理就是`用短的编码表示长的字符序列,用长的编码来表示短的字符序列`,
因此首先要做的就是统一字符序列的出现频率,然后根据统计的频率来构建huffman树(又叫最优二叉树)。
![huffman-tree](../assets/thinkings/huffman-tree.webp)
huffman 树就像是一个堆。 真正执行编码的时候,类似字典树,节点不用来编码,节点的路径用来编码
> 节点的值只是用来构建huffman 树
eg:
我们统计的结果如下:
```
character frequency
a 5
b 9
c 12
d 13
e 16
f 45
```
- 将每个元素构造成一个节点,即只有一个元素的树。并构建一个最小堆,包含所有的节点,该算法用了最小堆来作为优先队列。
- `选取两个权值最小的节点`,并添加一个权值为5+9=14的节点,作为他们的父节点。并`更新最小堆`
现在最小堆包含5个节点,其中4个树还是原来的节点,权值为5和9的节点合并为一个。
结果是这样的:
![huffman-example](../assets/thinkings/huffman-example.png)
```
character frequency encodeing
a 5 1100
b 9 1110
c 12 100
d 13 101
e 16 111
f 45 0
```
### run-length encode(游程编码)
游程编码是一种比较简单的压缩算法,其基本思想是将重复且连续出现多次的字符使用(连续出现次数,某个字符)来描述。
比如一个字符串:
```
AAAAABBBBCCC
```
使用游程编码可以将其描述为:
```
5A4B3C
```
5A表示这个地方有5个连续的A,同理4B表示有4个连续的B,3C表示有3个连续的C,其它情况以此类推。
但是实际上情况可能会非常复杂, 如何提取自序列有时候没有看的那么简单,还是上面的例子,我们
有时候可以把`AAAAABBBBCCC`整体看成一个子序列, 更复杂的情况还有很多,这里不做扩展。
对文件进行压缩比较适合的情况是文件内的二进制有大量的连续重复,
一个经典的例子就是具有大面积色块的BMP图像,BMP因为没有压缩,
所以看到的是什么样子存储的时候二进制就是什么样子
> 这也是我们图片倾向于纯色的时候,压缩会有很好的效果
> 思考一个问题, 如果我们在CDN上存储两个图片,这两个图片几乎完全一样,我们是否可以进行优化呢?
这虽然是CDN厂商更应该关心的问题,但是这个问题对我们影响依然很大,值得思考
### 总结
实际情况,我们先用游程编码一遍,然后再用huffman 再次编码一次。
### 相关题目
[900.rle-iterator](../problems/900.rle-iterator.md)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册