├── algorithmPuzzle
└── puzzle.md
├── .gitattributes
├── README.md
├── insertionSort
└── index.html
├── arrayToInsert
└── index.html
├── binarySearch
└── index.html
├── book
├── arrayToRepeat.md
├── insertionSort.md
├── bubbleSort.md
├── binarySearch.md
├── shellSort.md
└── quickSort.md
├── shellSort
└── index.html
├── bubbleSort
└── index.html
├── arrayToRepeat
└── index.html
└── quickSort
└── index.html
/algorithmPuzzle/puzzle.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.html linguist-language=javascript
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## JavaScript 算法集锦
2 | 使用 JavaScript 完成大多数的常用算法
3 |
4 | ------
5 |
6 | #### 参考书籍:
7 | > *《啊哈算法》
8 | > *《大话数据结构》
9 |
10 | ------
11 |
12 | ### 目录
13 | * [冒泡排序](https://github.com/jinzhuming/Algorithm/blob/master/book/bubbleSort.md)
14 | * [快速排序](https://github.com/jinzhuming/algorithm/blob/master/book/quickSort.md)
15 | * [二分查找](https://github.com/jinzhuming/algorithm/blob/master/book/binarySearch.md)
16 | * [数组去重](https://github.com/jinzhuming/algorithm/blob/master/book/arrayToRepeat.md)
17 | * [插入排序](https://github.com/jinzhuming/algorithm/blob/master/book/insertionSort.md)
18 | * [希尔排序](https://github.com/jinzhuming/algorithm/blob/master/book/shellSort.md)
19 |
--------------------------------------------------------------------------------
/insertionSort/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | insertionSort
8 |
9 |
10 |
11 | 有数组 a = [0, 1, 1, 3, 2, 11, 4, 3, 7]
12 | 执行插入排序
13 |
14 | 得到数组
15 |
16 | [0, 1, 1, 2, 3, 3, 4, 7, 11]
17 |
18 |
19 |
38 |
39 |
--------------------------------------------------------------------------------
/arrayToInsert/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | arrayToInsert
8 |
9 |
10 |
11 | 有数组 a = [1, 3, 0, 2, 4, 5, 6, 1, 213, 325, 6]
12 |
13 | 变量 b = 5
14 |
15 | 变量 c = 3
16 |
17 | 将变量 b(5) 插入 数组 a 的第 c(3) 个位置
18 |
19 | 得到数组
20 |
21 |
22 |
41 |
42 |
--------------------------------------------------------------------------------
/binarySearch/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | binarySearch
8 |
9 |
10 |
11 | 有数组 a = [1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11]
12 | 执行二分查找,寻找数字 9 所在的位置
13 |
14 | 得到 8
15 |
16 | a[9] === 9
17 |
18 |
19 |
42 |
43 |
--------------------------------------------------------------------------------
/book/arrayToRepeat.md:
--------------------------------------------------------------------------------
1 | > 数组去重(数组重复检查)是一个前端常用到的算法,有很多种方法,这里简单列出几种常用的方法。
2 |
3 | **如果只是简单的数组可以通过 遍历数组、indexOf、set 等方法去重**
4 |
5 | > 遍历数组
6 |
7 | ```
8 | const newArr = []
9 | for (let value of arr) {
10 | let state = false
11 | for (let newArrVal of newArr) {
12 | if (value === newArrVal) {
13 | // 重复
14 | state = true
15 | break
16 | }
17 | }
18 | if (!state) {
19 | newArr.push(value)
20 | }
21 | }
22 | return newArr
23 | ```
24 | 1. 构建一个新的数组。
25 | 2. 遍历一遍当前数组。
26 | 3. 遍历新数组。
27 | 4. 用当前数组中的每一个值和旧数组对比,发现重复则丢弃,否则插入新数组。
28 | 5. 遍历完成,返回新数组,即去重。
29 |
30 | 这种方法因为效率低下,所以最好不要采用
31 |
32 | > indexOf
33 | indexOf()方法返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回-1
34 |
35 | ```
36 | const newArr = []
37 | for (let value of arr) {
38 | if (newArr.indexOf(value) < 0) {
39 | newArr.push(value)
40 | }
41 | }
42 | return newArr
43 | ```
44 |
45 | 1. 简历新的数组。
46 | 2. 遍历当前数组。
47 | 3. 利用 indexOf 判断新数组内是否有当前元素,发现重复则丢弃,否则插入新数组
48 | 4. 遍历完成,返回新数组,即去重。
49 |
50 | 相对于遍历两次,使用 indexOf 更加简单
51 |
52 | > set Array.from
53 | Array.from() 是 es6 新增方法,用于将对象转化为真正的数组
54 | Set 用来生成一个新的数据结构,他的元素都是非重复的
55 |
56 | ```
57 | return Array.from(new Set(arr))
58 |
59 | ```
60 | 1. 将对象转化为真正的数组
61 | 2. 使用 Set 转化为没有重复的新结构
62 | -----
63 |
64 | 详细代码地址为 [数组去重](https://github.com/jinzhuming/algorithm/blob/master/arrayToRepeat/index.html)
65 |
--------------------------------------------------------------------------------
/shellSort/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | shellSort
8 |
9 |
10 |
11 | 有数组 a = [1, 3, 0, 5, 4, 2]
12 | 执行希尔排序
13 |
14 | 得到 新数组:
15 |
16 | [0, 1, 2, 3, 4, 5]
17 |
18 |
19 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/book/insertionSort.md:
--------------------------------------------------------------------------------
1 | >插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
2 | 
3 |
4 |
5 | ######算法描述
6 |
7 | ---
8 | 插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。
9 |
10 | 1. 从第一个元素开始,该元素可以认为已经被排序。
11 | 2. 取出下一个元素,在已经排序的元素序列中从后向前遍历判断大小。
12 | 3. 如果该元素(已排序)大于当前元素元素,将该元素移到下一位置
13 | 4. 重复步骤3,直到找到已排序的元素小于或者等于当前元素的位置
14 | 5. 将当前元素插入到该位置后
15 | 6. 重复步骤2~5
16 |
17 | ######算法复杂度
18 |
19 | ---
20 |
21 | 如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有 1/2n(n - 1)次。插入排序的**赋值操作**是比较操作的次数加上(n-1)次。平均来说插入排序算法复杂度为 O(n^2)。通常来讲,插入排序不适合大量数据的情况下使用。
22 |
23 | ###### javascript 实现
24 |
25 | ---
26 |
27 | ```
28 | let a = [0, 1, 1, 3, 2, 11, 4, 3, 7]
29 |
30 | Array.prototype.insertionSort = function() {
31 | // 遍历数组
32 | for (let i = 1; i < this.length; i += 1) {
33 | // 构建一个未排序序列,遍历已排序序列,判断大小找到应该插入的位置。
34 | for (let j = 0; j < i; j += 1) {
35 | if(this[j] > this[i]) {
36 | this.splice(j, 0, this[i])
37 | this.splice(i + 1, 1)
38 | }
39 | }
40 | }
41 | return this
42 | }
43 |
44 | a = a.insertionSort()
45 | ```
46 |
47 | 详细代码地址为 [插入排序](https://github.com/jinzhuming/Algorithm/blob/master/insertionSort/index.html/)
--------------------------------------------------------------------------------
/bubbleSort/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 | 有数组 a = [1, 3, 0, 2, 4, 5, 6, 1, 213, 325, 6]
12 | 执行冒泡排序
13 |
14 | 得到数组
15 |
16 |
17 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/book/bubbleSort.md:
--------------------------------------------------------------------------------
1 | > 冒泡排序(英语:Bubble Sort,台湾另外一种译名为:泡沫排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
2 | 冒泡排序对 n 个项目需要O( n^2)的比较次数,且可以原地排序。尽管这个算法是最简单了解和实现的排序算法之一,但它对于少数元素之外的数列排序是很没有效率的。
3 |
4 | **简单来说,冒泡排序的基本思想是,每次比较相邻的两个元素的大小,如果顺序错误就交互位置,通过进行n-1次lun**
5 |
6 | 冒泡排序算法的运作如下:
7 | 1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
8 | 2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
9 | 3. 针对所有的元素重复以上的步骤,除了最后一个。
10 | 4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
11 |
12 | 由于它的简洁,冒泡排序通常被用来对于程序设计入门的学生介绍算法的概念。
13 |
14 | -----
15 |
16 | 最后总结一下就是,如果 n 个数进行排序,就需要从第 0 个数字到 n 进行冒泡交换,交换一轮完成之后就可以再次执行第二轮,一直到执行 n - 1 轮,排序就完成了。js 代码如下:
17 |
18 | **伪代码**:
19 | ```
20 | // 确定数组
21 | const a = [1, 3, 0, 2, 4, 5, 6, 1, 213, 325, 6]
22 |
23 | const bubbleSort = (arr)=> {
24 |
25 | // 获取数组个数
26 | const length = arr.length
27 |
28 | // 一共执行 i = length - 1 次排序
29 | for (let i = 0; i < length; i++) {
30 |
31 | // 扣除掉每轮不需要比较的数组(第一轮倒数第一个,第二轮倒数第二个,第三轮...)
32 | for (let n = 0; n < length - 1 - i; n++) {
33 |
34 | // 如果前面比后面大,调换位置,用了 es 6 解构赋值语法。
35 | if (arr[n] > arr[n+1]) {
36 | [arr[n], arr[n + 1]] = [arr[n + 1], arr[n]]
37 | /*
38 | * 不采用解构赋值语法
39 | * var small = arr[n + 1]
40 | * arr[n + 1] = arr[n]
41 | * arr[n] = small
42 | */
43 | }
44 | }
45 | }
46 | return a
47 | }
48 | bubbleSort(a)
49 | ```
50 | 其实,只需要记住冒泡排序需要经过两次循环,两次的差别仅为第二次需要减去第一次循环获取到的变量,总体来讲还是很简单的。但是因为它是一宗比较慢的排序,实际中使用的并不多。
51 |
52 | >冒泡排序除了它迷人的名字和导致了某些有趣的理论问题这一事实之外,似乎没有什么值得推荐的。 ——Donald E.Knuth
53 |
54 | 详细代码地址为 [冒泡排序](https://github.com/jinzhuming/Algorithm/blob/master/bubbleSort/index.html/)
--------------------------------------------------------------------------------
/arrayToRepeat/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | arrayToRepeat
8 |
9 |
10 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/book/binarySearch.md:
--------------------------------------------------------------------------------
1 | >在计算机科学中,二分搜索(英语:binary search),也称折半搜索(英语:half-interval search)[1]、对数搜索(英语:logarithmic search)[2],是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
2 |
3 | 二分查找法主要是解决在 ***“一堆数中找出指定的数”*** 这类问题。
4 |
5 | 而想要应用二分查找法,这“一堆数”必须有一下特征:
6 |
7 | 1. 存储在数组中
8 | 2. 有序排列
9 |
10 | 所以如果是用**链表存储**的,就无法在其上应用二分查找法了。
11 |
12 | 至于是顺序递增排列还是递减排列,数组中是否存在相同的元素都不要紧。不过一般情况,我们还是希望并假设数组是递增排列,数组中的元素互不相同。
13 |
14 | **复杂度分析**
15 |
16 | [时间复杂度](https://zh.wikipedia.org/wiki/%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6)
17 |
18 | 折半搜索每次把搜索区域减少一半,时间复杂度为O(logn)。
19 |
20 | [空间复杂度](https://zh.wikipedia.org/wiki/%E8%A8%88%E7%AE%97%E8%A4%87%E9%9B%9C%E6%80%A7%E7%90%86%E8%AB%96)
21 |
22 | O(log1)虽以递归形式定义,但是尾递归,可改写为循环。
23 |
24 | **步骤**
25 |
26 | 给予一个包含n个带值元素的数组A或是记录A0 ... An−1,使得A0 ≤ An−1,以及目标值T,还有下列用来搜索T在A中位置的子程序。
27 |
28 | 令L为0,R为n− 1。
29 |
30 | 如果L > R,则搜索以失败告终。
31 |
32 | 令m(中间值元素)为“(L + R) / 2”加上下高斯符号。
33 |
34 | 如果Am < T,令L为m + 1并回到步骤二。
35 |
36 | 如果Am > T,令R为m - 1并回到步骤二。
37 |
38 | 当Am = T,搜索结束;回传值m。
39 |
40 | 这个迭代步骤会持续通过两个变量追踪搜索的边界。有些实际应用会在算法的
41 | 最后放入相等比较,让比较循环更快,但平均而言会多一层迭代[4]。
42 |
43 | ```
44 | const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
45 |
46 | // 给 Array 添加二分查找方法
47 | Array.prototype.binarySearch = function(low, high, khey) {
48 | if (low > high) {
49 | return -1
50 | }
51 |
52 | const mid = parseInt((high + low) / 2)
53 |
54 | if (this[mid] > khey) {
55 | return this.binarySearch(low, mid - 1, khey)
56 | }
57 |
58 | if (this[mid] < khey) {
59 | return this.binarySearch(mid + 1, high, khey)
60 | }
61 |
62 | return mid
63 | }
64 |
65 | a.binarySearch(0, a.length - 1, 9) // 8
66 | ```
67 | 详细代码地址为 [冒泡排序](https://github.com/jinzhuming/Algorithm/blob/master/binarySearch/index.html/)
--------------------------------------------------------------------------------
/quickSort/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | quickSort
8 |
9 |
10 |
11 | 有数组 a = [1, 3, 0, 5, 4, 2]
12 | 执行快速排序
13 |
14 | 得到 新数组:
15 |
16 | [0, 1, 2, 3, 4, 5]
17 |
18 |
19 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/book/shellSort.md:
--------------------------------------------------------------------------------
1 | > **希尔排序(英语:Shellsort)**,希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
2 |
3 | 希尔排序是基于插入排序的以下两点性质而提出改进方法的:
4 | * 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
5 | * 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
6 |
7 | **历史**
8 |
9 | 希尔排序按其设计者希尔(Donald Shell)的名字命名,该算法由1959年公布。一些老版本教科书和参考手册把该算法命名为Shell-Metzner,即包含Marlene Metzner Norton的名字,但是根据Metzner本人的说法,“我没有为这种算法做任何事,我的名字不应该出现在算法的名字中。”
10 |
11 | **算法实现**
12 |
13 | 希尔排序是基于插入排序的以下两点性质而提出改进方法的:
14 | 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
15 | 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
16 | 算法思路:
17 | 1. 先取一个正整数 d1(d1 < n),把全部记录分成 d1 个组,所有距离为 d1 的倍数的记录看成一组,然后在各组内进行插入排序
18 | 2. 然后取 d2(d2 < d1)
19 | 3. 重复上述分组和排序操作;直到取 di = 1(i >= 1) 位置,即所有记录成为一个组,最后对这个组进行插入排序。一般选 d1 约为 n/2,d2 为 d1 /2, d3 为 d2/2 ,…, di = 1。
20 |
21 | 例如:
22 | 数组 [9, 1, 0, 8, 7 ,5,2]
23 | 取 d1 = 3
24 | 分为 3组
25 | * 9, 8, 2
26 | * 1, 7
27 | * 0, 5
28 | 分别对其进行排序,得到结果
29 | [2, 1, 0, 8, 7, 5, 9]
30 |
31 | 取 d2 = 2
32 | 分为 2 组
33 | * 2, 0, 7, 9
34 | * 1, 8, 5
35 | 再次排序,得到
36 | [0, 1, 2, 5, 7, 8, 9]
37 |
38 | d3 = 1
39 | 进行一次插入排序
40 | 得到结果
41 | [0, 1, 2, 5, 7, 8, 9]
42 |
43 |
44 | **相关**
45 | [插入排序](https://github.com/jinzhuming/algorithm/blob/master/book/insertionSort.md)
46 |
47 | 代码实现
48 | ```
49 | let a = [1, 3, 0, 5, 4, 2]
50 |
51 | Array.prototype.shellSort = function() {
52 | function swap(array, i, k) {
53 | var temp = array[i];
54 | array[i] = array[k];
55 | array[k] = temp;
56 | }
57 |
58 | var length = this.length,
59 | gap = Math.floor(length / 2);
60 | while (gap > 0) {
61 | for (var i = gap; i < length; i++) {
62 | for (var j = i; 0 < j; j -= gap) {
63 | if (this[j - gap] > this[j]) {
64 | swap(this, j - gap, j);
65 | } else {
66 | break;
67 | }
68 | }
69 | }
70 | gap = Math.floor(gap / 2);
71 | }
72 |
73 | return this
74 | }
75 |
76 | a = a.shellSort()
77 | ```
78 | 详细代码地址为 [希尔排序](https://github.com/jinzhuming/Algorithm/blob/master/shellSort/index.html/)
79 |
80 |
--------------------------------------------------------------------------------
/book/quickSort.md:
--------------------------------------------------------------------------------
1 | > **快速排序(英语:Quicksort)**,又称划分交换排序(partition-exchange sort),一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序n个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
2 |
3 | 快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。
4 |
5 | **步骤为:**
6 |
7 | 从数列中挑出一个元素,称为"基准"(pivot),
8 | 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
9 | 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
10 | 递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
11 |
12 | **优化的排序算法**
13 |
14 | 快速排序是二叉查找树(二叉查找树)的一个空间最优化版本。不是循序地把数据项插入到一个明确的树中,而是由快速排序组织这些数据项到一个由递归调用所隐含的树中。这两个算法完全地产生相同的比较次数,但是顺序不同。对于排序算法的稳定性指标,原地分区版本的快速排序算法是不稳定的。其他变种是可以通过牺牲性能和空间来维护稳定性的。
15 |
16 | 快速排序的最直接竞争者是堆排序(Heapsort)。堆排序通常比快速排序稍微慢,但是最坏情况的运行时间总是O(n log n)。快速排序是经常比较快,除了introsort变化版本外,仍然有最坏情况性能的机会。如果事先知道堆排序将会是需要使用的,那么直接地使用堆排序比等待introsort再切换到它还要快。堆排序也拥有重要的特点,仅使用固定额外的空间(堆排序是原地排序),而即使是最佳的快速排序变化版本也需要Θ(log n)的空间。然而,堆排序需要有效率的随机存取才能变成可行。
17 |
18 | 快速排序也与归并排序(Mergesort)竞争,这是另外一种递归排序算法,但有坏情况O(n log n)运行时间的优势。不像快速排序或堆排序,归并排序是一个稳定排序,且可以轻易地被采用在链表(linked list)和存储在慢速访问媒体上像是磁盘存储或网络连接存储的非常巨大数列。尽管快速排序可以被重新改写使用在炼串列上,但是它通常会因为无法随机存取而导致差的基准选择。归并排序的主要缺点,是在最佳情况下需要Ω(n)额外的空间。
19 |
20 | **复杂度**
21 |
22 | 1. 平均复杂度
23 | 
24 |
25 | 2. 空间复杂度
26 |
27 | 最好
28 | 
29 |
30 | 最坏
31 | 
32 |
33 | 代码实现
34 | ```
35 | let a = [1, 3, 0, 5, 4, 2]
36 |
37 | Array.prototype.quickSort = function() {
38 | var len = this.length
39 |
40 | if (len <= 1) {
41 | return this.slice(0)
42 | }
43 |
44 | const left = []
45 | const right = []
46 | const mid = this.splice(0, 1)
47 |
48 | for (let key of this) {
49 | if (key < mid[0]) {
50 | left.push(key)
51 | }
52 | if (key >= mid[0]) {
53 | right.push(key)
54 | }
55 | }
56 |
57 | return left.quickSort().concat(mid.concat(right.quickSort()))
58 | }
59 |
60 | a = a.quickSort()
61 | ```
62 |
63 | 详细代码地址为 [冒泡排序](https://github.com/jinzhuming/Algorithm/blob/master/quickSort/index.html/)
64 |
--------------------------------------------------------------------------------