基于js的排序算法整理

目录

1、冒泡排序及优化

2、插入排序

3、希尔排序

4、选择排序

5、堆排序

6、快速排序及优化

7、归并排序

程序代码可以在github上下载哦https://github.com/ding-dang-cao!欢迎下载,如有错误,请指出,谢谢!

文参考文章:https://juejin.im/post/57dcd394a22b9d00610c5ec8作者分析得特别详细,如果大家对排序原理不太懂,强烈建议好好读读。大家可以多参考别人的文章,再整理出有自己特色的,自己容易理解的东西。

本文排序默认从小到大排列。

1、冒泡排序及优化

 1.1 冒泡排序原理

每趟排序比较相邻的两个数,如果前面的数大就交换它俩,一趟排完,最大的数都排在最后面,下一趟排序不需要考虑它,也就是每一趟排序的规模(需排序的数目)减1。

1.2  程序实现详解
   1、外层循环控制每一趟冒泡的元素个数,每冒泡一次,最大的元素在最末尾,下一趟冒泡的个数减1
   2、内层循环定义一趟冒泡要做的事情,将大的元素往后放,如果元素j>j+1,交换两个元素的位置
2.2 优化:
   1、如果已经排好了,可以让程序结束掉,不用再排。我们添加一个sortFlag,初始化为1,只要有交换 将其置0;如果某一趟冒泡,flag为1,就说明排好了

function bubbleSort1(arr) {
	console.time("1、冒泡排序耗时");
	var len = arr.length;
	for(var i = len - 1;i >= 2;i --) {
		for(var j = 0;j <= i;j ++) {
			if(arr[j] > arr[j+1]) {
				swap(arr,j,j + 1);
			}
		}
	}
	console.timeEnd("1、冒泡排序耗时")
	return arr;
}
function swap(arr,index1,index2) {
	var temp = arr[index1];
	arr[index1] = arr[index2];
	arr[index2] = temp;
	// 不用中间变量
	// a = a + b;
	// b = a - b;
	// a = a - b;
}
// 优化点1:设置sortFlag,当已经是顺序的时候不需要再排序了,break出来
function bubbleSort2(arr) {
	console.time("2、优化冒泡排序耗时");
	var len = arr.length;
	for(var i = len -1;i >= 2;i --) {
		var sortFlag = 0;
		for(var j = 0;j <= i;j ++) {
			if(arr[j] > arr[j+1]) {
				swap(arr,j,j+1);
				sortFlag = 1;
			}
		}
		if(sortFlag === 0)
			break;
	}
	console.timeEnd("2、优化冒泡排序耗时");
	return arr;
}
function createArr(length) {
	var arr = new Array();
	for(i = 0;i < length;i ++) {
		arr[i] = Math.floor(Math.random() * length) + 1;
	}
	return arr;
}
var testArr1 = createArr(100);
var testArr2 = createArr(100);
console.log(bubbleSort1(testArr1));
console.log(bubbleSort2(testArr2));

2、插入排序

2.1 插入排序(从小到大)原理

将插入想象成摸一手牌,每摸进来一张牌j,要依次和手中已有牌(从后往前扫描)比较大小,如果手中的牌比摸进来的牌大,手中的牌往后挪一位(j-1的值放在i的位置),否则,摸进来的牌就放在i的位置。

2.2  程序实现详解
   1、依次摸入;对于数组元素0到N-1;假定下标0已经在手上,从下标1开始摸牌到N-1,将摸进来的值存在临时变量
   2、旧牌让位;内层循环负责判断前面的牌要不要挪位,将临时变量与已经排好序的序列从后向前扫描地比较大小,当满足新牌i>=1并且arr[j-1]>arr[j]的时候,给新牌让位置,arr[j] = arr[j-1];   
  3、 新牌落位;从内层循环跳出来的情况,不需要挪位或者已经挪好位,直接插进去,arr[j] = temp;

2.3 优化
   1、插入排序每一次交换都是相邻的两个元素,每次只能消除一个逆序对,我们可以跳着交换,这样可能一次可以消除几个逆序对,引出了后面的希尔排序,采用增量序列。

function insertionSort(arr) {
	var len = arr.length;
	for(var i = 1;i <= len -1;i ++) {
		var temp = arr[i];
		for(var j = i;j >= 1 && arr[j-1] > temp;j --) {		
			arr[j] = arr[j-1];
		}
		arr[j] = temp;
	}
	return arr;
}
function createArr(length) {
	var arr = new Array();
	for(i = 0;i < length;i ++) {
		arr[i] = Math.floor(Math.random() * length) + 1;
	}
	return arr;
}
var testArr1 = createArr(100);
console.log(insertionSort(testArr1));
// console.log(bubbleSort2(testArr1));

3、希尔排序

3.1 希尔排序(从小到大)原理

希尔排序可以理解为对插入排序的优化,插入排序每一次交换都是相邻的两个元素,每次只能消除一个逆序对,我们可以跳着交换,这样可能一次可以消除几个逆序对。

3.2  程序实现详解
   1、外层循环控制着增量序列,增量序列最好采用互质序列(递减至1),我们用一个数组来维护,取1,3,5
   2、内层循环就是对每一个增量,完成该序列的插入排序 
 

function heapSort(arr) {
	var stepArr = [5,3,1];
	var len = arr.length;
	for(var m = 0;m <= 2;m ++){
		var step = stepArr[m];
		for(var i = m;i <= len-1;i ++){
			var temp = arr[i];
			for(var j = i;j >= step && arr[j-step] > temp;j -= step){
				arr[j] = arr[j-step]
			}
			arr[j] = temp;
		}
	}
	return arr;
}
function createArr(length) {
	var arr = new Array();
	for(i = 0;i < length;i ++) {
		arr[i] = Math.floor(Math.random() * length) + 1;
	}
	return arr;
}
var testArr1 = createArr(100);
console.log(heapSort(testArr1));
// console.log(bubbleSort2(testArr2));

4、选择排序

4.1 选择排序(从小到大)原理

每次找出未排序序列中最小的元素放在序列起始位置,然后在未排序列中继续找最小的元素放在已排序列的末尾; 依次类推。

4.2  程序实现详解

    1、外层for循环控制无序列表的第一个元素i,从0到N-1
    2、内层for循环找出无序列表的最小元素索引min
    3、交换i和min索引所在位置的值

4.3 优化
   堆排序是对选择排序的优化,利用小顶堆找出最小元素的索引

function selectionSort(arr) {
	var len = arr.length;
	for(i = 0;i <= len-1;i ++) {
		var min = i;
		for(j = i + 1;j <= len - 1;j ++) {
			if(arr[j] < arr[min]) 
				min = j;
		}
		swap(arr,min,i);
	}
	return arr;
}
function swap(arr,index1,index2) {
	var temp = arr[index1];
	arr[index1] = arr[index2];
	arr[index2] = temp;
	// 不用temp
	// arr[index1] = arr[index1] + arr[index2];
	// arr[index2] = arr[index1] - arr[index2];
	// arr[index1] = arr[index1] - arr[index2];
}
function createArr(length) {
	var arr = new Array();
	for(i = 0;i < length;i ++) {
		arr[i] = Math.floor(Math.random() * length) + 1;
	}
	return arr;
}
var testArr1 = createArr(100);
console.log(selectionSort(testArr1));
// console.log(bubbleSort2(testArr2));

5、堆排序 

5.1 堆排序(从小到大)原理

1、建堆;利用树这种数据结构,将数组元素看成是按索引顺序排列成完全二叉树,现在将其调整成大顶堆(对于树中任意一个节点,父节点的值大于左右儿子的值);注意,此时没有设哨兵,根节点就是数组第一个元素;

2、将堆的最大值(大顶堆的根节点)与堆的最后一个元素互换,并将堆的规模减1(最后一个元素最大,不需要在排序),然后调整为最大堆;

3、不断重复2,程序运行完毕,数组已经是从小到大排列了

5.2  程序实现详解

    1、N个元素的树,最后一个有儿子的结点是Math.floor(len / 2) - 1,从这个结点开始调整称大顶堆,从下往上,直到整个树形成大顶堆;
    2、从最后一个元素开始,将大顶堆的根元素与其互换,再调整堆
    3、调整堆的程序详解:对与父节点parent,左儿子结点2*parent + 1,右儿子2*parent + 2.。如果有儿子,先找出最大的儿子;再将根节点与最大的儿子比较,把最大的值放在根节点上。有点类似于选择排序。

function heapSort(arr) {
	var len = arr.length;
	for(var i = Math.floor(len / 2) - 1;i >= 0;i --) {
		adjustHeap(arr,i,len);// 将len个元素的数组中以arr[i]为根的
		// 子堆调成最大堆
	}// 创建堆,从最后一个根节点开始,遍历所有根节点,调成大顶堆
	for(var j = len - 1;j > 0;j --) {
		swap(arr,0,j); // 将0位置的最大元素放到最后一位
		adjustHeap(arr,0,j);// 将j个元素的数组中以arr[0]为根的
		// 子堆调成大顶堆
	}
	return arr;
}
function swap(arr,index1,index2) {
	var temp = arr[index1];
	arr[index1] = arr[index2];
	arr[index2] = temp;
	// 不用temp
	// arr[index1] = arr[index1] + arr[index2];
	// arr[index2] = arr[index1] - arr[index2];
	// arr[index1] = arr[index1] - arr[index2];
}
function adjustHeap(arr,root,N) {
	var temp = arr[root]; // 取出根节点的值
	var child;
	for(parent = root;(parent * 2 + 1) <= N-1;parent = child) {
		child = 2 * parent + 1; // 左儿子
		if((child != N -1) && (arr[child] < arr[child + 1]))
			child ++; // child指向的是最大的儿子,当child不是N-1,
		// 说明有右儿子,再比较他俩大小。
		if(temp >= arr[child]) break;
		else
			arr[parent] = arr[child];
	}
	arr[parent] = temp;
}
function createArr(length) {
	var arr = new Array();
	for(i = 0;i < length;i ++) {
		arr[i] = Math.floor(Math.random() * length) + 1;
	}
	return arr;
}
var testArr1 = createArr(100);
console.log(heapSort(testArr1));
// console.log(bubbleSort2(testArr2));

6、快速排序及优化

6.1 快速排序算法(从小到大)过程:
     1、选择一个基准元素(主元),将列表分成两个独立的子序列
     2、对列表重新排序,将所有小于基准值的元素放在基准值的前面,大于pivot的值放在后面
     3、对比较小的子序列和比较大的子序列重复上述过程
6.2 优化思路:
     1、选取主元,一般选第一个元素,但如果第一个元素是最大或最小的元素,会出现一边倒的情况,每一趟都干不了实事;我们选取头中尾三位数,取他们的中位数
     2、对小规模的数据还不如用插入排序,可以设定一个阈值, 大于这个值才用快排 

 6.3快排的特点:每一次根据主元划分完两个子集后,主元呆在了它最应该呆的位置  

function fastSort(arr,left,right) {
	if(left < right) {
		var pivot = arr[left];// 以第一个数为基准
		i = left ;
		j = right + 1;
		while(true) {
			while(arr[++i] < pivot) {};
			while(arr[--j] > pivot) {};
			if(i < j){
				swap(arr,i,j);				
			} else 
			break;
		}
		swap(arr,left,j);
		fastSort(arr,left,j - 1);
		fastSort(arr,j + 1,right);
	}
	return arr;
}
// 优化:将头中尾三个数的中位数选为主元,并并将主元放在right-1
// 的位置,因为这三个数已经排好序,left和right不用参与排序
// 需要比较的数是left+1到right-2
function fastSort2(arr,left,right) {
	if(left < right) {
		var pivot = selectPivot(arr,left,right);
		var i = left;
		var j = right - 1;
		while(i < j) {
			while(arr[++i] < pivot){};
			while(arr[--j] > pivot){};
			if(i < j) {
				swap(arr,i,j)
			}
		}
		swap(arr,i,right - 1);
		fastSort2(arr,left,i - 1);
		fastSort2(arr,i + 1,right);
	}
	return arr;
}
// 取中位数为主元并并将主元放在right-1的位置 
function selectPivot(arr,left,right) {
	var middle = Math.floor((left + right) / 2);
	if(arr[middle] < arr[left]) {
		swap(arr,left,middle);
	}
	if(arr[right] < arr[left]) {
		swap(arr,right,left);
	}
	if(arr[right] < arr[middle]) {
		swap(arr,right,middle);
	}
	swap(arr,middle,right - 1);
	return arr[right - 1];
}
function swap(arr,index1,index2) {
	var temp = arr[index1];
	arr[index1] = arr[index2];
	arr[index2] = temp;
	// 不用temp
	// arr[index1] = arr[index1] + arr[index2];
	// arr[index2] = arr[index1] - arr[index2];
	// arr[index1] = arr[index1] - arr[index2];
}
function createArr(length) {
	var arr = new Array();
	for(i = 0;i < length;i ++) {
		arr[i] = Math.floor(Math.random() * length) + 1;
	}
	return arr;
}
var testArr = createArr(20);
console.log(fastSort(testArr,0,19));

7、归并排序

7.1 归并排序思路:将序列不断分成两个子序列,将两个有序的子序列合并;
7.2、具体做法:1、长度为n的序列,分成两个长度为n/2的序列
                           2、对这两个子序列进行递归,继续二分,直到len = 1
                           3、将两个排序好的子序列合并成最终的序列;
划分是从上到下的,合并是从下到上的

function mergeSort(arr) {
	var len = arr.length;
	if(len <= 1) {
		return arr;
	}
	var middle = Math.floor(len / 2);
	var left = arr.slice(0,middle);
	var right = arr.slice(middle);
	return merge(mergeSort(left),mergeSort(right));
}
function merge(left,right) {
	var result = [];
	while(left.length && right.length) {
		if(left[0] <= right[0]) {
			result.push(left.shift());
		}else{
			result.push(right.shift());
		}
	}
	while(left.length) {
		result.push(left.shift());
	}
	while(right.length) {
		result.push(right.shift());
	}
	return result;
}
function createArr(length) {
	var arr = [];
	for(i = 0;i < length;i ++) {
		arr[i] = Math.floor(Math.random() * length) + 1;
	}
	return arr;
}
var testArr = createArr(15);
console.log(mergeSort(testArr));

猜你喜欢

转载自blog.csdn.net/xuli1207516504/article/details/82217972