Skip to content

Commit 95b68be

Browse files
committed
add heap sort
1 parent 3b5cbd3 commit 95b68be

File tree

3 files changed

+119
-38
lines changed

3 files changed

+119
-38
lines changed

README.md

+12-4
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,21 @@ js中可以通过对象的哈希结构来实现树结构,两种数据结构结
4242

4343
### sort [sort.js](/js/sort.js)
4444

45-
#### bubble:冒泡排序
45+
#### [bubbleSort:冒泡排序](/js/sort.js#L20)
4646

47-
#### select:选择排序
47+
#### [selectSort:选择排序](/js/sort.js#L36)
4848

49-
#### quick:快速排序
49+
#### [straightInsertionSort: 直接插入排序](/js/sort.js#L52)
5050

51-
#### merge:归并排序
51+
#### [shellSort:希尔排序](/js/sort.js#L72)
52+
53+
#### [quickSort:快速排序](/js/sort.js#L101)
54+
55+
#### [inPlaceQuickSort:原地快速排序](/js/sort.js#L127)
56+
57+
#### [mergeSort:归并排序](/js/sort.js#L159)
58+
59+
#### [heapSort:堆排序](/js/sort.js#L192)
5260

5361

5462

js/sort.js

+98-30
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,26 @@
77

88
'use strict';
99

10+
/**
11+
* 交换数组中两元素位置
12+
* @param : i, j: 待交换的两元素下标
13+
*/
14+
Array.prototype.swap = function(i, j) {
15+
const temp = this[i];
16+
this[i] = this[j];
17+
this[j] = temp;
18+
}
19+
1020
/**
1121
* 冒泡排序
1222
* @param : <Array> target数组
1323
* @description : 冒泡排序,更贴切的形容应该是沉底排序,每一轮内循环就是最大数沉底了。
1424
*/
1525
module.exports.bubbleSort = function bubbleSort(target) {
16-
var temp;
1726
for(var j = target.length; j > 0; j--) {
1827
for(var i = 0; i < j - 1; i++) {
1928
if(target[i] > target[i + 1]) {
20-
temp = target[i];
21-
target[i] = target[i + 1];
22-
target[i + 1] = temp;
29+
target.swap(i, i + 1);
2330
}
2431
}
2532
}
@@ -37,9 +44,7 @@ module.exports.selectSort = function selectSort(target) {
3744
for(var i = 1; i < j; i++) {
3845
maxIndex = target[maxIndex] > target[i] ? maxIndex : i;
3946
}
40-
var temp = target[j - 1];
41-
target[j - 1] = target[maxIndex];
42-
target[maxIndex] = temp;
47+
target.swap(maxIndex, j - 1);
4348
}
4449
return target;
4550
};
@@ -124,31 +129,30 @@ module.exports.quickSort = function quickSort(target) {
124129
* @param : <Array> target
125130
* @description : 上面的快排每次都开辟一个数组,浪费空间。常规做法是两边查找到中间,两两交换位置
126131
*/
127-
module.exports.inPlaceQuickSort = function inPlaceQuickSort(target) {
128-
function _inPlaceQuickSort(target, left, right) {
129-
// 先定义递归终止条件
130-
if(left >= right) { return target; }
131-
132-
var base = target[left];
133-
var i = left;
134-
var j = right;
135-
while(i < j) {
136-
while(i < j && target[j] >= base) {
137-
j--;
138-
}
139-
target[i] = target[j];
140-
while(i < j && target[i] <= base) {
141-
i++;
142-
}
143-
target[j] = target[i];
132+
function _inPlaceQuickSort(target, left, right) {
133+
// 先定义递归终止条件
134+
if(left >= right) { return target; }
135+
136+
var base = target[left];
137+
var i = left;
138+
var j = right;
139+
while(i < j) {
140+
while(i < j && target[j] >= base) {
141+
j--;
144142
}
145-
target[i] = base;
146-
// 也可以利用函数副作用改变传入数组,但是用显式返回更清晰
147-
target = _inPlaceQuickSort(target, left, i - 1);
148-
target = _inPlaceQuickSort(target, i + 1, right);
149-
return target;
143+
target[i] = target[j];
144+
while(i < j && target[i] <= base) {
145+
i++;
146+
}
147+
target[j] = target[i];
150148
}
151-
149+
target[i] = base;
150+
// 函数副作用已经改变了传入数组,但是用显式返回看起来更清晰
151+
target = _inPlaceQuickSort(target, left, i - 1);
152+
target = _inPlaceQuickSort(target, i + 1, right);
153+
return target;
154+
}
155+
module.exports.inPlaceQuickSort = function inPlaceQuickSort(target) {
152156
return _inPlaceQuickSort(target, 0, target.length - 1)
153157
};
154158

@@ -185,4 +189,68 @@ module.exports.mergeSort = function mergeSort(target) {
185189
return mergeSortedArray(mergeSort(left), mergeSort(right));
186190
};
187191

192+
/**
193+
* 堆排序
194+
* @param : <Array> target
195+
* @description : 通过构建大(小)根堆的方式进行排序,PS:使用函数副作用来进行原地排序
196+
*/
197+
// 递归调整 i~j 层的大根堆
198+
function adjustMaxHeap(target, i, j) {
199+
let parent = i;
200+
let left = 2 * i + 1;
201+
let right = 2 * i + 2;
188202

203+
// 比较父节点与左右叶子节点,取最大值的下标设为父节点下标
204+
if(left < j && target[parent] < target[left]) {
205+
parent = left;
206+
}
207+
if(right < j && target[parent] < target[right]) {
208+
parent = right;
209+
}
210+
// 只有父节点发生改变才会破坏大根堆结构,此时才需要继续调整下级大根堆
211+
if(parent != i) {
212+
target.swap(i, parent);
213+
adjustMaxHeap(target, parent, j);
214+
}
215+
}
216+
// 构建大根堆就是不断调整最大堆的过程,只要从最后一个父节点往上调整到第一个父节点,就能构建出大根堆
217+
// 从0开始的n层堆的结构:len = 2^n - 1,第n层全是叶子,所以第n-1层的最后一个父节点就是floor(len/2)-1
218+
function buildMaxHeap(target) {
219+
const len = target.length;
220+
for(let i = Math.floor(len / 2) - 1; i >= 0; i--) {
221+
adjustMaxHeap(target, i, len);
222+
}
223+
}
224+
225+
function sortMaxHeap(target) {
226+
for(let i = target.length - 1; i > 0; i--) {
227+
target.swap(0, i);
228+
adjustMaxHeap(target, 0, i);
229+
}
230+
}
231+
// 先构建一个大根堆,然后从最后一个元素开始交换堆顶元素,每次交换都调整根堆,直到数组头则完成排序
232+
module.exports.heapSort = function heapSort(target) {
233+
buildMaxHeap(target);
234+
sortMaxHeap(target);
235+
return target;
236+
};
237+
238+
/**
239+
* 堆排序提取部分记录
240+
* 从大数据中提取最大(小)的n条记录,也可以用小(大)根堆来实现
241+
* 先用数据集中前n条数据构造一个小根堆,然后将后面的数据依次通过这个小根堆:
242+
* 比堆顶小的数据直接丢弃,比堆顶大则替换堆顶,然后调整根堆。最后输出小根堆的排序
243+
*/
244+
module.exports.topSortViaHeap = function topSortViaHeap(target) {
245+
const len = 10;
246+
let ret = target.slice(0, len);
247+
buildMaxHeap(ret);
248+
for(var i = len; i < target.length; i++) {
249+
if(target[i] < ret[0]) {
250+
ret[0] = target[i];
251+
adjustMaxHeap(ret, 0, ret.length);
252+
}
253+
}
254+
sortMaxHeap(ret);
255+
return ret;
256+
}

js/sort.test.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,24 @@ function compareArray(a, b) {
4040
return true;
4141
}
4242

43+
// 以冒泡排序为基准测试
4344
function main() {
44-
const origin = makeRandomArray(20);
45+
const origin = makeRandomArray(30);
4546
let sorted;
46-
4747
for(let name in sorts) {
4848
logger.info(`======== ${name} ========`);
4949
if(!sorted) {
5050
sorted = sorts[name](origin.slice(0));
5151
isArraySort(sorted);
5252
} else {
5353
const s = sorts[name](origin.slice(0));
54-
compareArray(sorted, s)
55-
? logger.ok('success: ' + JSON.stringify(s))
54+
let isOk;
55+
if(name === 'topSortViaHeap') {
56+
isOk = compareArray(sorted.slice(0, 10), s);
57+
} else {
58+
isOk = compareArray(sorted, s);
59+
}
60+
isOk ? logger.ok('success: ' + JSON.stringify(s))
5661
: logger.error('fail: ' + JSON.stringify(s))
5762
}
5863
}

0 commit comments

Comments
 (0)