Last active
June 29, 2021 08:29
-
-
Save ideawu/a114679bb8f0a94452d462ae14b7c977 to your computer and use it in GitHub Desktop.
快速排序QuickSort算法JavaScript实现, 包括 Hoare 和 Lomuto 版本的实现,以及网友实现版本的对比
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
<script> | |
// @author: ideawu | |
// @link: http://www.ideawu.net/blog/archives/1021.html | |
var swap_count = 0; | |
var cmp_count = 0; | |
// https://gist.github.com/wintercn/c30464ed3732ee839c3eeed316d73253 | |
function wintercn_qsort(arr, start, end){ | |
var midValue = arr[start]; | |
var p1 = start, p2 = end; | |
while(p1 < p2) { | |
swap(arr, p1, p1 + 1); | |
while(compare(arr[p1], midValue) >= 0 && p1 < p2) { | |
swap(arr, p1, p2--); | |
} | |
p1 ++; | |
} | |
if(start < p1 - 1) | |
wintercn_qsort(arr, start, p1 - 1); | |
if(p1 < end) | |
wintercn_qsort(arr, p1, end); | |
} | |
// http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html | |
var ruanyifeng_qsort = function(arr) { | |
if (arr.length <= 1) { return arr; } | |
var pivotIndex = Math.floor(arr.length / 2); | |
var pivot = arr.splice(pivotIndex, 1)[0]; | |
swap_count++; | |
var left = []; | |
var right = []; | |
for (var i = 0; i < arr.length; i++){ | |
if (compare(arr[i], pivot) < 0) { | |
swap_count++; | |
left.push(arr[i]); | |
} else { | |
swap_count++; | |
right.push(arr[i]); | |
} | |
} | |
swap_count++; | |
return ruanyifeng_qsort(left).concat([pivot], ruanyifeng_qsort(right)); | |
} | |
function compare(a, b){ | |
cmp_count ++; | |
return a-b; | |
} | |
function swap(arr, s, e){ | |
swap_count ++; | |
var tmp = arr[s]; | |
arr[s] = arr[e]; | |
arr[e] = tmp; | |
} | |
function partition_hoare(arr, start, end){ | |
var pivot = arr[start]; | |
var s = start; | |
var e = end; | |
while(1){ | |
while(compare(arr[s], pivot) < 0){ | |
s ++; | |
} | |
while(compare(arr[e], pivot) > 0){ | |
e --; | |
} | |
if(s == e){ | |
return s; | |
}else if(s > e){ | |
return s-1; | |
} | |
swap(arr, s, e); | |
s++; | |
e--; | |
} | |
} | |
function qsort_hoare(arr, start, end){ | |
if(start >= end){ | |
return; | |
} | |
var p = partition_hoare(arr, start, end); | |
qsort_hoare(arr, start, p); | |
qsort_hoare(arr, p+1, end); | |
} | |
function partition_lomuto(arr, start, end){ | |
var pivot = arr[end]; | |
var s = start; | |
for(var g = start; g < end; g++){ | |
if(compare(arr[g], pivot) < 0){ | |
if(s != g){ | |
swap(arr, s, g); | |
} | |
s ++; | |
} | |
} | |
if(s == end){ | |
return s-1; | |
}else{ | |
swap(arr, s, end); | |
return s; | |
} | |
} | |
function qsort_lomuto(arr, start, end){ | |
if(start >= end){ | |
return; | |
} | |
// console.log('in', arr, start, end); | |
var p = partition_lomuto(arr, start, end); | |
// console.log(JSON.stringify(arr.slice(start, p+1)), JSON.stringify(arr.slice(p+1, end+1))); | |
qsort_lomuto(arr, start, p); | |
qsort_lomuto(arr, p+1, end); | |
} | |
function bubble_sort(arr, start, end){ | |
for(var i=start; i<=end; i++){ | |
for(var j=start; j<=end-i-1; j++){ | |
if(compare(arr[j], arr[j+1]) > 0){ | |
swap(arr, j, j+1); | |
} | |
} | |
} | |
} | |
function check_result(a){ | |
var pass = true; | |
var v = a[0]; | |
for(var j=0; j<a.length; j++){ | |
var n = a[j]; | |
if(n < v){ | |
pass = false; | |
break; | |
} | |
v = a[j]; | |
} | |
return pass; | |
} | |
function run_test(func, name){ | |
document.write('<b>run test: ', name, '</b><br/>'); | |
swap_count = 0; | |
cmp_count = 0; | |
var nlogn = 0; | |
var stime = (new Date()).getTime(); | |
for(var i=1; i<=arr.length; i++){ | |
var a = arr.slice(0, i); | |
var b = func(a, 0, a.length - 1); | |
if(func == ruanyifeng_qsort){ | |
a = b; | |
} | |
if(!check_result(a)){ | |
console.log(i, 'fail'); | |
console.log(a); | |
} | |
nlogn += Math.floor(i*Math.log(i)); | |
// console.log('n:', i, 'swaps:', swap_count, 'compares:', cmp_count, 'nlogn:', Math.floor(i*Math.log(i)), JSON.stringify(a)); | |
} | |
var etime = (new Date()).getTime(); | |
document.write('time: ', etime-stime, ' ms', '<br/>'); | |
document.write('compare: ', cmp_count, ' swap: ', swap_count, ' nlogn: ', nlogn, '<br/>'); | |
document.write('<br/>'); | |
} | |
var count = 2000; | |
var arr = []; | |
for(var i=0; i<count; i++){ | |
arr.push(Math.floor(Math.random() * count)); | |
} | |
console.log('datasource:', JSON.stringify(arr)); | |
document.write('Running...'); | |
setTimeout(function(){ | |
document.write('<pre>'); | |
run_test(wintercn_qsort, 'wintercn_qsort'); | |
run_test(ruanyifeng_qsort, 'ruanyifeng_qsort'); | |
run_test(qsort_hoare, 'qsort_hoare'); | |
run_test(qsort_lomuto, 'qsort_lomuto'); | |
document.write('</pre>'); | |
// document.write('Running bubble_sort...'); | |
// setTimeout(function(){ | |
// document.write('<pre>'); | |
// run_test(bubble_sort, 'bubble_sort'); | |
// document.write('</pre>'); | |
// }, 0); | |
}, 0); | |
</script> | |
</body> | |
</html> |
再来个非递归版,和递归版其实是一样的,compare和swap数量应该与递归版完全相同(因为计算过程完全一致),耗时也许能少一丢丢?
function qsort_loop(arr, start, end) {
var _start = start;
var _end = end;
var stack = [];
while (1) {
var midValue = arr[_start];
var h = _start + 1;
var t = _end;
while (h <= t) {
while (h <= t && compare(arr[h], midValue) <= 0) {
h++;
}
if (h > t) {
break;
}
while (t >= h && compare(midValue, arr[t]) <= 0) {
t--;
}
if (t < h) {
break;
}
swap(arr, h, t);
h++;
t--;
}
swap(arr, _start, t);
if (h < _end) {
stack.push(h);
stack.push(_end);
}
if (t - 1 > _start) {
_end = t - 1;
} else if (stack.length > 0) {
_end = stack.pop();
_start = stack.pop();
} else {
break;
}
}
}
耗时排名稳定在第一或者第二的位置,weibo:光挣钱不说话
function shiye515Sort(arr, start, end) {
let divider = end;
let dividerValue = arr[divider]
let blank = divider
let pickStart = start
let pickEnd = end - 1
let isAsc = true
while (pickStart <= pickEnd) {
if (isAsc) {
if (compare(arr[pickStart], dividerValue) > 0) {
swap(arr, blank, pickStart);
blank = pickStart
isAsc = false
}
pickStart++
} else {
if (compare(arr[pickEnd], dividerValue) < 0) {
swap(arr, blank, pickEnd);
blank = pickEnd
isAsc = true
}
pickEnd--
}
}
let mid = isAsc ? (pickEnd + 1) : (pickStart - 1)
if (start < mid - 1) {
shiye515Sort(arr, start, mid - 1)
}
if (mid + 1 < end) {
shiye515Sort(arr, mid + 1, end)
}
}
为啥没人提到要先shuffle下?quicksort最坏复杂度不是O^2吗?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
贴个基础版: