# JS 排序算法:冒泡、选择、插入、归并、快速、希尔、堆、计数
# 1. 冒泡排序算法实现(javascript)
//冒泡排序算法(javascript)
//author:Hengda
//arr数组
//mode false 升序 ture 降序
function bubbleSort( arr, mode ){
var i, j, temp, len = arr.length;
for( i = len - 1 ; i > 0; i-- ){
for( j = 0; j < i; j++ ){
if( mode ? arr[ j + 1 ] < arr[ j ] : arr[ j + 1 ] > arr[ j ] ){
temp = arr[ j + 1 ];
arr[ j + 1 ] = arr[ j ];
arr[ j ] = temp;
}
}
}
return arr;
}
2. 计数排序算法实现(javascript)
//计数排序算法(javascript)
//author:Hengda
//arr数组
//mode false 升序 ture 降序
function countingSort( arr, mode ){
//i,j为控制变量,temp为交换变量,len为数组的长度
var i, j, temp, len = arr.length;
var countArr = [];//用于原始数组中统计各元素出现的次数
var fillPos;//标记下一个回填位置
var countArrLen;//计数数组的长度
//统计
for( i = 0; i < len; i++ ){
if( countArr[ arr[ i ] ] != null ){
countArr[ arr[ i ] ] ++;
}else{
countArr[ arr[ i ] ] = 1;
}
}
//将数据重新排列回填到原始数组中
//统计
var fillPos = 0;//回填起始位置
var countArrLen = countArr.length;
if( mode ){
//
for( i = countArrLen - 1; i >=0; i-- ){
//
if( countArr[ i ] != null ){
//回填countArr[ i ]个当前值i到原始数组,回填起始位置为fillPos
for( j = 0; j < countArr[ i ]; j++ ){
arr[ fillPos++ ] = i;
}
}
}
}else{
//
for( i = 0; i < countArrLen; i++ ){
//
if( countArr[ i ] != null ){
//回填countArr[ i ]个当前值i到原始数组,回填起始位置为fillPos
for( j = 0; j < countArr[ i ]; j++ ){
arr[ fillPos++ ] = i;
}
}
}
}
//排序完成
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 3. 堆排序算法实现(javascript)
//功能: 堆排序(javascript)
//author: Hengda
//arr: 待排序数组
//mode: true 从大到小排序,false 从小到大排序
function heapSort(arr, mode) {
var len = arr.length; //数组的长度
var temp; //用于交换节点值
var endHeapNodeNo; //堆末尾节点在数组中的下标
//将数组调整为二叉堆
for (var i = Math.floor(len / 2) - 1; i >= 0; i--) {
heapNodeSink(arr, i, len, mode);
}
for (var heapLen = len; heapLen > 0; heapLen--) {
endHeapNodeNo = heapLen - 1; //堆的最后一个节点的序号
//交换堆顶和堆尾元素
temp = arr[endHeapNodeNo];
arr[endHeapNodeNo] = arr[0];
arr[0] = temp;
//对除了堆尾元素组成的堆进行堆顶下沉操作
heapNodeSink(arr, 0, heapLen - 1, mode);
}
return arr;
}
//堆中某节点按升序或者降序递归下沉
//author: Hengda
//arr: 待排序数组
//nodeNo: 二叉树中指定节点的序号/堆数组中的下标
//heapLen: 堆的长度
//mode: true 大的下沉,false 小的下沉
function heapNodeSink(arr, nodeNo, heapLen, mode) {
var leftChild = (nodeNo + 1) * 2 - 1; //做孩子
var rightChild = leftChild + 1; //右孩子
var maxminNo = nodeNo; //最大值的序号
var temp; //用户变量值得交换
if (mode) {
//
if (heapLen > leftChild && arr[maxminNo] > arr[leftChild]) {
maxminNo = leftChild; //更新最大节点序号
}
if (heapLen > rightChild && arr[maxminNo] > arr[rightChild]) {
maxminNo = rightChild; //更新最大节点序号
}
} else {
if (heapLen > leftChild && arr[maxminNo] < arr[leftChild]) {
maxminNo = leftChild; //更新最大节点序号
}
if (heapLen > rightChild && arr[maxminNo] < arr[rightChild]) {
maxminNo = rightChild; //更新最大节点序号
}
}
//最大值所在节点有变化,则交换
if (maxminNo != nodeNo) {
//交换
temp = arr[maxminNo];
arr[maxminNo] = arr[nodeNo];
arr[nodeNo] = temp;
//继续下沉操作
heapNodeSink(arr, maxminNo, heapLen, mode);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 4. 插入排序算法实现(javascript)
//插入排序算法(javascript)
//算法原理
//author:Hengda
//2020/1/25
//arr 待排序数组
//mode true 从大到小排列,false 从小到大排列
function insertionSort(arr, mode) {
var i,
j,
temp,
len = arr.length; //len为待排序数组长度 temp为交换变量 i j为控制变量。
//从数组的第二个元素开始逐个往后处理。
for (i = 1; i < len; i++) {
//将当前被处理元素值记录下来。
temp = arr[i];
//以下标倒序逐一比较当前元素位置之前的所有元素,如果比当前元素大,则逐一向后覆盖一个元素。
for (j = i - 1; j >= 0 && (mode ? arr[j] < temp : arr[j] > temp); j--) {
arr[j + 1] = arr[j];
}
//将点前被处理元素的值填入最终空缺的位置即 (j + 1) 注意这个 j 已经被for循环做了-1操作,所以这里需要+1。
arr[j + 1] = temp;
}
//遍历完成后,整个数组即为有序数组。
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 5. 归并排序算法实现(javascript)
//归并排序算法(javascript)
//author:Hengda
//arr数组
//start 数组中待排序段落的起止位置,len为数据段的长度
//mode false 升序 ture 降序
function mergeSort(arr, start, len, mode) {
var i, j, temp;
//计算左侧数据段的位置和长度
var lstart = start;
var llen = Math.floor(len / 2);
//计算右侧数据段的位置和长度
var rstart = lstart + llen;
var rlen = len - llen;
//分别对左右分段进行进行插入排序
if (llen > 4) mergeSort(arr, lstart, llen);
if (rlen > 4) mergeSort(arr, rstart, rlen);
//对当前数据段进行插入排序
for (i = start + 1; i < len + start; i++) {
temp = arr[i];
for (j = i - 1; j >= start && (mode ? arr[j] < temp : arr[j] > temp); j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 6. 选择排序算法实现(javascript)
//选择排序算法(javascript)
//author:Hengda
//arr数组
//mode false 升序 ture 降序
function selectionSort(arr, mode) {
//i,j为控制变量,miniMaxNo为标记发现的最大或者最小元素的下标,temp为交换变量,len为数组的长度
var i,
j,
minMaxNo,
temp,
len = arr.length;
//
for (i = 0; i < len; i++) {
//当前位置初始为最小或最大数的位置
minMaxNo = i;
//遍历后续所有元素与minMaxNo对应的元素做比较,如果比minMaxNo大或者小,则更新minMaxNo的值为新元素的下标
for (j = i; j < len; j++) {
if (mode ? arr[j] > arr[minMaxNo] : arr[j] < arr[minMaxNo]) {
minMaxNo = j;
}
}
//将最终确定的最大或者最小值与当前被处理位置i对应的元素值做交换
temp = arr[minMaxNo];
arr[minMaxNo] = arr[i];
arr[i] = temp;
}
//排序完成
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 7. 希尔排序算法实现(javascript)
//希尔排序算法(javascript)
//author:Hengda
//arr数组
//mode false 升序 ture 降序
function shellSort(arr, mode) {
//1,j,k为控制变量,gap为分组间隙初始化为1,temp用于交换数据,len数组的长度
var i,
j,
k,
gap = 1,
temp,
len = arr.length;
//计算合适的分组元素间隙,这里计算得到gap的最大值,这里的5也可以是其他数,值不同,实际排序速度也不同
while (gap < len / 5) {
gap = gap * 5 + 1;
}
//开始排序
while (gap > 0) {
//以下按分组排序,该排序原理为插入排序,如看不明白,可参考插入排序算法逻辑
for (i = gap; i < len; i++) {
temp = arr[i];
for (
j = i - gap;
j >= 0 && (mode ? arr[j] < temp : arr[j] > temp);
j -= gap
) {
arr[j + gap] = arr[j];
}
arr[j + gap] = temp;
}
//缩小分组间隔值
gap = Math.floor(gap / 5);
}
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 8. 快速排序算法实现(javascript)
//快速排序(javascript)
//author:Hengda
//arr数组
//start 待排序数据段的起始下标(含)
//end 待排序数据段终止下标(含)
//mode false 升序 ture 降序
function quickSort(arr, start, end, mode) {
var i, j, temp;
var divValue;
if (start < end) {
//初始化基准值
baseValue = arr[end];
j = start;
//遍历整段数据元素,小于等于基准值的放在基准准直左侧(正序),大于等于基准值的放在基准值左侧(倒序)
for (i = start; i <= end; i++) {
//与基准值作比较
if (mode ? arr[i] >= baseValue : arr[i] <= baseValue) {
//从左端开始依次排列放置,当前排列位置为$j,把原位置的元素向后交换
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
//更新下一个应排列的位置
j++;
}
}
//循环中$j在最后的++操作并未使用,这里需要减去,使$j正确标记左右分界元素
j--;
//分界元素在两端是,则靠近分界元素的一端无需再排序
//分界元素也无需再参与排序,因为左侧的一定小于等于分界元素,右侧的也一定大于等于分界元素
//分别对分界元素左右两侧的数据段继续排序
if (j > start) quickSort(arr, start, j - 1, mode);
if (j < end) quickSort(arr, j + 1, end, mode);
}
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38