Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: trekhleb/javascript-algorithms
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 52a2c21b7d4e4c874b1f555143acd5f505464197
Choose a base ref
..
head repository: trekhleb/javascript-algorithms
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: de57051cd2ddd6fc3318c88505e239fb987c19b9
Choose a head ref
18 changes: 9 additions & 9 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -22,21 +22,21 @@ _Read this in other languages:_

`B` - 初学者, `A` - 进阶

* `B` [链表](src/data-structures/linked-list)
* `B` [双向链表](src/data-structures/doubly-linked-list)
* `B` [队列](src/data-structures/queue)
* `B` [](src/data-structures/stack)
* `B` [哈希表](src/data-structures/hash-table)
* `B` [](src/data-structures/heap)
* `B` [优先队列](src/data-structures/priority-queue)
* `B` [链表](src/data-structures/linked-list/README.zh-CN.md)
* `B` [双向链表](src/data-structures/doubly-linked-list/README.zh-CN.md)
* `B` [队列](src/data-structures/queue/README.zh-CN.md)
* `B` [](src/data-structures/stack/README.zh-CN.md)
* `B` [哈希表](src/data-structures/hash-table/README.zh-CN.md)
* `B` [](src/data-structures/heap/README.zh-CN.md)
* `B` [优先队列](src/data-structures/priority-queue/README.zh-CN.md)
* `A` [字典树](src/data-structures/trie)
* `A` [](src/data-structures/tree)
* `A` [](src/data-structures/tree/README.zh-CN.md)
* `A` [二叉查找树](src/data-structures/tree/binary-search-tree)
* `A` [AVL 树](src/data-structures/tree/avl-tree)
* `A` [红黑树](src/data-structures/tree/red-black-tree)
* `A` [线段树](src/data-structures/tree/segment-tree) - 使用 最小/最大/总和 范围查询示例
* `A` [树状数组](src/data-structures/tree/fenwick-tree) (二叉索引树)
* `A` [](src/data-structures/graph) (有向图与无向图)
* `A` [](src/data-structures/graph/README.zh-CN.md) (有向图与无向图)
* `A` [并查集](src/data-structures/disjoint-set)
* `A` [布隆过滤器](src/data-structures/bloom-filter)

99 changes: 99 additions & 0 deletions src/data-structures/doubly-linked-list/README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# 双向链表

在计算机科学中, 一个 **双向链表(doubly linked list)** 是由一组称为节点的顺序链接记录组成的链接数据结构。每个节点包含两个字段,称为链接,它们是对节点序列中上一个节点和下一个节点的引用。开始节点和结束节点的上一个链接和下一个链接分别指向某种终止节点,通常是前哨节点或null,以方便遍历列表。如果只有一个前哨节点,则列表通过前哨节点循环链接。它可以被概念化为两个由相同数据项组成的单链表,但顺序相反。

![Doubly Linked List](https://upload.wikimedia.org/wikipedia/commons/5/5e/Doubly-linked-list.svg)

两个节点链接允许在任一方向上遍历列表。

在双向链表中进行添加或者删除节点时,需做的链接更改要比单向链表复杂得多。这种操作在单向链表中更简单高效,因为不需要关注一个节点(除第一个和最后一个节点以外的节点)的两个链接,而只需要关注一个链接即可。



## 基础操作的伪代码

### 插入

```text
Add(value)
Pre: value is the value to add to the list
Post: value has been placed at the tail of the list
n ← node(value)
if head = ø
head ← n
tail ← n
else
n.previous ← tail
tail.next ← n
tail ← n
end if
end Add
```

### 删除

```text
Remove(head, value)
Pre: head is the head node in the list
value is the value to remove from the list
Post: value is removed from the list, true; otherwise false
if head = ø
return false
end if
if value = head.value
if head = tail
head ← ø
tail ← ø
else
head ← head.Next
head.previous ← ø
end if
return true
end if
n ← head.next
while n = ø and value = n.value
n ← n.next
end while
if n = tail
tail ← tail.previous
tail.next ← ø
return true
else if n = ø
n.previous.next ← n.next
n.next.previous ← n.previous
return true
end if
return false
end Remove
```

### 反向遍历

```text
ReverseTraversal(tail)
Pre: tail is the node of the list to traverse
Post: the list has been traversed in reverse order
n ← tail
while n = ø
yield n.value
n ← n.previous
end while
end Reverse Traversal
```

## 复杂度

## 时间复杂度

| Access | Search | Insertion | Deletion |
| :-------: | :-------: | :-------: | :-------: |
| O(n) | O(n) | O(1) | O(1) |

### 空间复杂度

O(n)

## 参考

- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
22 changes: 22 additions & 0 deletions src/data-structures/graph/README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#

在计算机科学中, **图(graph)** 是一种抽象数据类型,
旨在实现数学中的无向图和有向图概念,特别是图论领域。

一个图数据结构是一个(由有限个或者可变数量的)顶点/节点/点和边构成的有限集。

如果顶点对之间是无序的,称为无序图,否则称为有序图;

如果顶点对之间的边是没有方向的,称为无向图,否则称为有向图;

如果顶点对之间的边是有权重的,该图可称为加权图。



![Graph](https://www.tutorialspoint.com/data_structures_algorithms/images/graph.jpg)

## 参考

- [Wikipedia](https://en.wikipedia.org/wiki/Graph_(abstract_data_type))
- [Introduction to Graphs on YouTube](https://www.youtube.com/watch?v=gXgEDyodOJU&index=9&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
- [Graphs representation on YouTube](https://www.youtube.com/watch?v=k1wraWzqtvQ&index=10&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
21 changes: 21 additions & 0 deletions src/data-structures/hash-table/README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 哈希表

在计算中, 一个 **哈希表(hash table 或hash map)** 是一种实现 *关联数组(associative array)*
的抽象数据;类型, 该结构可以将 *键映射到值*

哈希表使用 *哈希函数/散列函数* 来计算一个值在数组或桶(buckets)中或槽(slots)中对应的索引,可使用该索引找到所需的值。

理想情况下,散列函数将为每个键分配给一个唯一的桶(bucket),但是大多数哈希表设计采用不完美的散列函数,这可能会导致"哈希冲突(hash collisions)",也就是散列函数为多个键(key)生成了相同的索引,这种碰撞必须
以某种方式进行处理。


![Hash Table](https://upload.wikimedia.org/wikipedia/commons/7/7d/Hash_table_3_1_1_0_1_0_0_SP.svg)

通过单独的链接解决哈希冲突

![Hash Collision](https://upload.wikimedia.org/wikipedia/commons/d/d0/Hash_table_5_0_1_1_1_1_1_LL.svg)

## 参考

- [Wikipedia](https://en.wikipedia.org/wiki/Hash_table)
- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
96 changes: 56 additions & 40 deletions src/data-structures/heap/Heap.js
Original file line number Diff line number Diff line change
@@ -144,44 +144,51 @@ export default class Heap {

/**
* @param {*} item
* @param {Comparator} [customFindingComparator]
* @param {Comparator} [comparator]
* @return {Heap}
*/
remove(item, customFindingComparator) {
remove(item, comparator = this.compare) {
// Find number of items to remove.
const customComparator = customFindingComparator || this.compare;
const numberOfItemsToRemove = this.find(item, customComparator).length;
const numberOfItemsToRemove = this.find(item, comparator).length;

for (let iteration = 0; iteration < numberOfItemsToRemove; iteration += 1) {
// We need to find item index to remove each time after removal since
// indices are being change after each heapify process.
const indexToRemove = this.find(item, customComparator).pop();
// indices are being changed after each heapify process.
const indexToRemove = this.find(item, comparator).pop();
this.removeIndex(indexToRemove);
}

return this;
}

// If we need to remove last child in the heap then just remove it.
// There is no need to heapify the heap afterwards.
if (indexToRemove === (this.heapContainer.length - 1)) {
this.heapContainer.pop();
/**
* @param {number} indexToRemove
* @return {Heap}
*/
removeIndex(indexToRemove) {
// If we need to remove last child in the heap then just remove it.
// There is no need to heapify the heap afterwards.
if (indexToRemove === (this.heapContainer.length - 1)) {
this.heapContainer.pop();
} else {
// Move last element in heap to the vacant (removed) position.
this.heapContainer[indexToRemove] = this.heapContainer.pop();

// Get parent.
const parentItem = this.parent(indexToRemove);

// If there is no parent or parent is in correct order with the node
// we're going to delete then heapify down. Otherwise heapify up.
if (
this.hasLeftChild(indexToRemove)
&& (
!parentItem
|| this.pairIsInCorrectOrder(parentItem, this.heapContainer[indexToRemove])
)
) {
this.heapifyDown(indexToRemove);
} else {
// Move last element in heap to the vacant (removed) position.
this.heapContainer[indexToRemove] = this.heapContainer.pop();

// Get parent.
const parentItem = this.hasParent(indexToRemove) ? this.parent(indexToRemove) : null;
const leftChild = this.hasLeftChild(indexToRemove) ? this.leftChild(indexToRemove) : null;

// If there is no parent or parent is in incorrect order with the node
// we're going to delete then heapify down. Otherwise heapify up.
if (
leftChild !== null
&& (
parentItem === null
|| this.pairIsInCorrectOrder(parentItem, this.heapContainer[indexToRemove])
)
) {
this.heapifyDown(indexToRemove);
} else {
this.heapifyUp(indexToRemove);
}
this.heapifyUp(indexToRemove);
}
}

@@ -190,12 +197,11 @@ export default class Heap {

/**
* @param {*} item
* @param {Comparator} [customComparator]
* @param {Comparator} [comparator]
* @return {Number[]}
*/
find(item, customComparator) {
find(item, comparator = this.compare) {
const foundItemIndices = [];
const comparator = customComparator || this.compare;

for (let itemIndex = 0; itemIndex < this.heapContainer.length; itemIndex += 1) {
if (comparator.equal(item, this.heapContainer[itemIndex])) {
@@ -206,6 +212,15 @@ export default class Heap {
return foundItemIndices;
}

/**
*
* @param {number} index
* @return {*}
*/
getElementAtIndex(index) {
return this.heapContainer[index];
}

/**
* @return {boolean}
*/
@@ -224,9 +239,9 @@ export default class Heap {
* @param {number} [customStartIndex]
*/
heapifyUp(customStartIndex) {
// Take last element (last in array or the bottom left in a tree) in
// a heap container and lift him up until we find the parent element
// that is less then the current new one.
// Take the last element (last in array or the bottom left in a tree)
// in the heap container and lift it up until it is in the correct
// order with respect to its parent element.
let currentIndex = customStartIndex || this.heapContainer.length - 1;

while (
@@ -241,10 +256,11 @@ export default class Heap {
/**
* @param {number} [customStartIndex]
*/
heapifyDown(customStartIndex) {
// Compare the root element to its children and swap root with the smallest
// of children. Do the same for next children after swap.
let currentIndex = customStartIndex || 0;
heapifyDown(customStartIndex = 0) {
// Compare the parent element to its children and swap parent with the appropriate
// child (smallest child for MinHeap, largest child for MaxHeap).
// Do the same for next children after swap.
let currentIndex = customStartIndex;
let nextIndex = null;

while (this.hasLeftChild(currentIndex)) {
19 changes: 19 additions & 0 deletions src/data-structures/heap/README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 堆 (数据结构)

在计算机科学中, 一个 ** 堆(heap)** 是一种特殊的基于树的数据结构,它满足下面描述的堆属性。

在一个 *最小堆(min heap)* 中, 如果 `P``C` 的一个父级节点, 那么 `P` 的key(或value)应小于或等于 `C` 的对应值.

![最小堆](https://upload.wikimedia.org/wikipedia/commons/6/69/Min-heap.png)

在一个 *最大堆(max heap)* 中, `P` 的key(或value)大于 `C` 的对应值。

![](https://upload.wikimedia.org/wikipedia/commons/3/38/Max-Heap.svg)


在堆“顶部”的没有父级节点的节点,被称之为根节点。

## 参考

- [Wikipedia](https://en.wikipedia.org/wiki/Heap_(data_structure))
- [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
19 changes: 16 additions & 3 deletions src/data-structures/linked-list/README.md
Original file line number Diff line number Diff line change
@@ -37,7 +37,20 @@ Add(value)
end if
end Add
```


```text
Prepend(value)
Pre: value is the value to add to the list
Post: value has been placed at the head of the list
n ← node(value)
n.next ← head
head ← n
if tail = ø
tail ← n
end
end Prepend
```

### Search

```text
@@ -76,10 +89,10 @@ Remove(head, value)
end if
return true
end if
while n.next = ø and n.next.value = value
while n.next != ø and n.next.value != value
n ← n.next
end while
if n.next = ø
if n.next != ø
if n.next = tail
tail ← n
end if
Loading