前端进修:教程&开辟模块化/规范化/工程化/优化&东西/调试&值得关注的博客/Git&口试-前端资本汇总
迎接提issues指正:排序算法
JavaScript-排序算法及浅易优化
疾速排序
道理:在待排序序列当选一个支解元素,将待排序序列分隔成自力的子序列,子序列1里的元素比支解元素元素都小(大),子序列2反之,递归举行此操纵,以到达子序列都有序。末了将子序列用concat要领连接起来等于排序好的序列。
function quickSort(arr){
if(arr.length <= 1){
return arr;
}
var num = Math.floor(arr.length/2);
var numValue = arr.splice(num,1);
var left = [];
var right = [];
for(var i = 0;i<arr.length;i++){
if(arr[i] < numValue){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort(left).concat(numValue,quickSort(right));
}
console.log(quickSort([1,45,43,4,56,7,20,1]));
//[1, 1, 4, 7, 20, 43, 45, 56]
优化:当待排序序列长度N < 10时,运用插进去排序,能够加快排序。
function quickSort(arr){
if(arr.length <= 1){
return arr;
}
if(arr.length < 10){
insertSort(arr);
}
var num = Math.floor(arr.length/2);
var numValue = arr.splice(num,1);
var left = [];
var right = [];
for(var i = 0;i<arr.length;i++){
if(arr[i] < numValue){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort(left).concat(numValue,quickSort(right));
}
console.log(quickSort([1,3,4,645,43,4,56,333,44,564,7,20,1]));
//[1, 1, 3, 4, 4, 7, 20, 43, 44, 56, 333, 564, 645]
插进去排序
道理:经由过程构建有序序列,关于未排序元素,在已排序序列中从后向前扫描,找到响应位置并插进去。平常能够将第一个元素作为有序序列,用未排序的元素与之比拟,插进去,直到排序终了。
function insertSort(arr){
var len = arr.length;
if(len <= 1){
return arr;
}
for(var i = 1;i<len;i++){
var tmp = arr[i];
var j = i;
while(arr[j-1] > tmp){
arr[j] = arr[j-1];
--j;
}
arr[j] = tmp;
}
return arr;
}
console.log(insertSort([1,45,43,4,56,7,20,1]));
//[1, 1, 4, 7, 20, 43, 45, 56]
优化:二分插进去排序,在有序序列中运用二分查找法查找一个插进去位置,削减元素比较次数
function binaryInsertSort(arr){
var len = arr.length;
if(len <= 1){
return arr;
}
for (var i = 1; i < len; i++) {
var tmp = arr[i], left = 0, right = i - 1;
while (left <= right) {
var index = parseInt((left + right) / 2);
if (tmp < arr[index]) {
right = index - 1;
} else {
left = index + 1;
}
}
for (var j = i - 1; j >= left; j--) {
arr[j + 1] = arr[j];
}
arr[left] = tmp;
}
return arr;
}
console.log(binaryInsertSort([1,45,43,4,56,7,20,1,2,3,4,56,3]));
//[1, 1, 2, 3, 3, 4, 4, 7, 20, 43, 45, 56, 56]
挑选排序
道理:在待排序序列中找到最小(大)元素,放在序列的肇端位置,然后,再从盈余元素中寻觅最小(大)元素,然后放到已排序序列的末端。反复,直到一切元素均排序终了。
Array.prototype.selectionSort = Array.prototype.selectionSort || function(){
var len = this.length;
if(len <= 1){
return this;
}
var min,tmp;
for(var i = 0;i<len;i++){
min = i;
for(var j = i+1;j<len;j++){
if(this[j] < this[min]){
min = j;
}
}
if(i != min){
tmp = this[i];
this[i] = this[min];
this[min] = tmp;
}
}
return this;
}
console.log([1,45,43,4,56,7,20,1].selectionSort());
//[1, 1, 4, 7, 20, 43, 45, 56]
优化:堆排序,在直接挑选排序中,为了从序列当选出关键字最小(最大)的纪录,必需举行n-1次比较,接着在剩下序列当选出最小(最大)的元素,又须要做n-2次比较。然则,在n-2次比较中,有的比较能够在前面的n-1次比较中已做过,但由于前一趟排序时未保留这些比较效果,所今后一趟排序时又反复执行了这些比较操纵。聚集是一个近似完整二叉树的构造,并同时满足聚集的性子:即子结点的键值或索引老是小于(或许大于)它的父节点。堆排序可经由过程树形构造保留部份比较效果,可削减比较次数。
function heapSort(arr) {
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function maxHeapify(arr, index, heapSize) {
var iMax,iLeft,iRight;
while (true) {
iMax = index;
iLeft = 2 * index + 1;
iRight = 2 * (index + 1);
if (iLeft < heapSize && arr[index] < arr[iLeft]) {
iMax = iLeft;
}
if (iRight < heapSize && arr[iMax] < arr[iRight]) {
iMax = iRight;
}
if (iMax != index) {
swap(arr, iMax, index);
index = iMax;
} else {
break;
}
}
}
function buildMaxHeap(arr) {
var i,iParent = Math.floor(arr.length / 2) - 1;
for (i = iParent; i >= 0; i--) {
maxHeapify(arr, i, arr.length);
}
}
function sort(arr) {
buildMaxHeap(arr);
for (var i = arr.length - 1; i > 0; i--) {
swap(arr, 0, i);
maxHeapify(arr, 0, i);
}
return arr;
}
return sort(arr);
}
console.log(heapSort([1,45,43,4,56,7,20,1,2,3,4,56,3]));
//[1, 1, 2, 3, 3, 4, 4, 7, 20, 43, 45, 56, 56]
冒泡排序
道理:从第一个元素最先,一次比较两个元素,假如arr[n]大于arr[n+1],就交流。反复遍历直到没有再须要交流,排序完成。
function bubbleSort(arr){
var len = arr.length;
if(len <= 1){
return arr;
}
var tmp;
for(var i = 0;i<len;i++){
for(var j =0;j<len-1-i;j++){
if(arr[j+1] < arr[j]){
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
return arr;
}
console.log(bubbleSort([1,45,43,4,56,7,20,1]));
//[1, 1, 4, 7, 20, 43, 45, 56]
优化:上面代码中,内里一层轮回在某次扫描中没有发作交流,申明此时数组已悉数排好序,背面的步骤就无需再执行了。因而,增添一个标记,每次发作交流,就标记,假如某次轮回完没有标记,则申明已完成排序。
function bubbleSort(arr) {
var len = arr.length;
if(len <= 1){
return arr;
}
var bSwaped = true;
for (var i = 0; i < len -1; i++) {
// 每次先重置为false
bSwaped = false;
for (var j = len - 1; j > i ; j--) {
if (arr[j-1] > arr[j]) {
var temp = arr[j-1];
arr[j-1] = arr[j];
arr[j] = temp;
bSwaped = true;
}
}
// 假如上一次扫描没有发作交流,则申明数组已悉数有序,退出轮回
if (!bSwaped){
break;
}
}
return arr;
}
console.log(bubbleSort([1,45,43,4,56,7,20,1]));
//[1, 1, 4, 7, 20, 43, 45, 56]
在上一步优化的基础上进一步思索:假如R[0..i]已经是有序区间,上次的扫描区间是R[i..n],记上次扫描时末了一次发作交流的位置是lastSwapPos,那末lastSwapPos会在在i与n之间,所以R[i..lastSwapPos]区间是有序的,不然这个区间也会发作交流;所以下次扫描区间就能够由R[i..n]缩减到[lastSwapPos..n] :
function bubbleSort(arr){
var len = arr.length;
if(len <= 1){
return arr;
}
var lastSwapPos = 0, lastSwapPosTemp = 0;
for (var i = 0; i < len - 1; i++){
lastSwapPos = lastSwapPosTemp;
for (var j = len - 1; j > lastSwapPos; j--){
if (arr[j - 1] > arr[j]){
var temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
lastSwapPosTemp = j;
}
}
if (lastSwapPos == lastSwapPosTemp){
break;
}
}
return arr;
}
console.log(bubbleSort([1,45,43,4,56,7,20,1]));
//[1, 1, 4, 7, 20, 43, 45, 56]
这一些列优化都须要测速才晓得有无优化胜利,只是简朴的测试一两个数组是不容易看出来的。我们能够造一些很大的数据去测试,再用一个比较简朴的测试时候的要领,随机建立10万个数:
var arr = [];
var num = 0;
for(var i = 0; i < 100000; i++){
num = Math.floor(Math.random()*100000);
arr.push(num);
}
console.time("testTime");
bubbleSort(arr);
console.timeEnd("testTime");
==> testTime: 21900.684ms (比较数量越多,差异越大,更好对照)