数据结构-经典排序算法实现及其可视化(JavaScript实现)

实现经典的9种排序算法,分析其时间复杂度以及空间复杂度,并用动画的方式演示。

常见的算法时间复杂度由小到大依次为:

Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n^2)<Ο(n^3)<…<Ο(2^n)<Ο(n!)





一、 直接插入排序

步骤:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5

JavaScript实现:

var insertSort = function(arr){
	for(let i=1;i<arr.length;i++){
		let temp = arr[i];
		for(var j=i-1;j>=0;j--){
			if(arr[j] > temp){
				arr[j+1] = arr[j];
				arr[j] = temp;
			}else{
				break;//找到比temp小的则跳出循环
			}
		}
		arr[j+1] = temp;//在比temp小的值后面插入temp值
	}
	return arr;
时间复杂度:

最好: O(n)

最坏: O(n^2)

平均: O(n^2)

动态图显示:



二、希尔排序

希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  1. 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性
  2. 排序的效率但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。

JavaScript实现:

//希尔排序
var shellSort = function(arr,type,showSort){
	var half = parseInt(arr.length/2);
	for(let d=half;d>=1;d=parseInt(d/2) ){
		for(let i=d;i<arr.length;i++){
			for(let j=i-d;j>=0;j-=d){
				if(arr[j+d] < arr[j]){
					let tem = arr[j+d];
					arr[j+d] = arr[j];
					arr[j] = tem;
				}
			}
		}
	}
	return arr;
}

时间复杂度:

最好: O(n*log2n)

最坏: O(n^2)


三、冒泡排序

步骤:

  1. 比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

JavaScript实现:

var bubbleSort = function(arr){
	for(let i=1;i<arr.length;i++){
		for(let j=0;j<arr.length-i;j++){
			if(arr[j] > arr[j+1]){
				let tem = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = tem;
			}
		}
	}
	return arr;
}

时间复杂度:

最好: O(n)

最坏: O(n^2)


四、快速排序

步骤:

快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列,又称为分区交换排序。步骤为:

  1. 从序列中挑出一个元素,作为"轴值".
  2. 把所有比轴值小的元素放在轴值前面,所有比轴值大的元素放在轴值的后面,这个称为分区操作。
  3. 对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。

JavaScript实现:

//快速排序
var quickSort = function(arr,type,showSort){
	//快速排序
	var quick = function(out,first,end){
		if(first<end){
			let i=first, j=end, temp=0;
			//一个循环完成一趟扫描
			while(i<j){
				while(i<j&& out[i]< out[j]){
					j--;
				}
				if(i<j){
					temp = out[i];
					out[i] = out[j];
					out[j] = temp;
					i++;
				}
				while(i<j&& out[i] < out[j]){
					i++;
				}
				if(i<j){
					temp = out[i];
					out[i] = out[j];
					out[j] = temp;
					j--;
				}
			}
			quick(out,first,i-1);
			quick(out,i+1,end);
		}
		return out;
	}
	return quick(arr,0,arr.length-1);
}

时间复杂度:

最好:O(n)

平均: O(n*log2n)

最坏: O(n^2)


五、选择排序

        选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
  注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。

JavaScript实现:

//选择排序
var selectSort = function(arr,type,showSort){
	for(let i=0;i<arr.length;i++){
		let index = i;
		for(let j=i;j<arr.length;j++){
			if(arr[j] < arr[index]){
				index = j;
			}
		}
		let temp = arr[i];
		arr[i] = arr[index];
		arr[index] = temp;
	}
	return arr;
}

时间复杂度:

最好: O(n^2)

最坏: O(n^2)


六、堆排序

堆排序是指利用堆这种数据结构所设计的一种选择排序算法。堆是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),并满足性质:以最大堆(也叫大根堆、大顶堆)为例,其中父结点的值总是大于它的孩子节点。

我们可以很容易的定义堆排序的过程:
  1. 由输入的无序数组构造一个最大堆,作为初始的无序区
  2. 把堆顶元素(最大值)和堆尾元素互换
  3. 把堆(无序区)的尺寸缩小1,并调用heapify(A, 0)从新的堆顶元素开始进行堆调整
  4. 重复步骤2,直到堆的尺寸为1

JavaScript实现:

//堆排序
var heapSort = function(arr,type,showSort){
	var len = arr.length;
	//建立堆
	var sift = function(out, k, m){
		let i = k, j = 2*k+1;
		while(j <= m && j!=len){
			if(j<m && out[j+1] && out[j] < out[j+1]){
				j++;
			}
			if(out[i] > out[j]){
				break;
			}else{
				let temp = out[i];
				out[i] = out[j];
				out[j] = temp;
				i = j;
				j = 2*i+1;
			}
		}
	}
	let half = parseInt(len/2);
	//初始建堆
	for(let i=half-1;i>=0;i--){
		sift(arr, i, len);
	}
	for(let i=0;i<len-1;i++){
		let temp = arr[0];
		arr[0] = arr[len-1-i];
		arr[len-1-i] = temp;
		sift(arr, 0, len-1-i-1);
	}
	return arr;
}

时间复杂度:

最好: O(n*log2n)

最坏: O(n*log2n)


七、归并排序

归并排序的实现分为递归实现与非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,一直下去直到归并了整个数组。

归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

JavaScript实现:

//归并排序
var mergeSort = function(arr,type,showSort){
	//一次归并算法
	var merge = function(out, array, s, m, t){
		let i=s, j=m+1, k=s;//i:左边数组的索引,j:右边数组的索引,k:归并结果数组的索引
		//没有数组遍历完
		while(i<=m && j<=t){
			if(out[i] < out[j]){
				array[k++] = out[i++];
			}else{
				array[k++] = out[j++];
			}
		}
		//如果左数组没有遍历完,将左数组剩余数据压入arr
		if(i<=m){
			while(i<=m){
				array[k++] = out[i++];
			}
		}else{
			while(j<=t){
				array[k++] = out[j++];
			}
		}
		return array;
	}
	//一趟归并排序算法
	var mergePass = function(out, array, h){
		var len = out.length;
		let i = 0;
		while(i+2*h<=len){
			merge(out, array, i , i+h-1, i+2*h-1);
			i += 2*h;
		}
		if(i+h<len){
			merge(out, array, i, i+h-1, len-1);
		}else{
			while(i<len){
				array[i] = out[i];
				i++;
			}
		}
		return array;
	}
	//非递归归并排序
	var mergeSortUnrecursion = function(out){
		var len = out.length;
		var array = [];
		for(let i=0;i<arr.length;i++){
			array[i] = out[i];
		}
		var h = 1;
		while(h<len){
			mergePass(out, array, h);
			h = 2*h;
			mergePass(array, out, h);
			h = 2*h;
		}
		return out;
	}
	//递归归并排序
	var mergeSortRecursion = function(out,array, s, t){
		if(s === t){
			array[s] = out[s];
		}else{
			let m = parseInt((s+t)/2);
			mergeSortRecursion(out, array, s, m);
			mergeSortRecursion(out, array, m+1, t);
			merge(array, out, s, m, t);
			//将out复制给array,继续下一轮归并
			for(let i=0;i<out.length;i++){
				array[i] = out[i];
			}
		}
		return out;
	}
	var array = [];
	return mergeSortRecursion(arr,array, 0, arr.length-1);
}

时间复杂度:

非递归

最好: O(n)

最坏: O(n^2)


八、桶排序

原理:

假设待排序的值都在 0~m-1 之间,设置 m 个桶(bucket),首先将值为 i 的值分配(distribute)到第 i 个桶中,然后将各个桶中的记录依次收集(collect)起来。

JavaScript实现:

//桶排序
var bucketSort = function(arr){
	var distribute = function(arr){
		var bucket = [];
		for(let i=0;i<arr.length;i++){
			let m = arr[i];
			if(bucket[m] === undefined){
				bucket[m] = 1;
			}else{
				bucket[m]++;
			}
		}
		return bucket;
	}
	var collect = function(bucket){
		var out = [], index = 0;
		for(let i=0;i<bucket.length;i++){
			let temp = bucket[i];
			while(temp>=1){
				out[index++] = i;
				temp--;
			}
		}
		return out;
	}
	var buckets = distribute(arr);
	var out = collect(buckets);
	return out;
}

时间复杂度: O(n+m)

空间复杂度: O(m)


九、基数排序

基数排序是借助多关键码进行桶排序的思想进行排序。分为最主位优先 (MSD) 和最次位优先 (LSD) 。

以最次位优先为例:


JavaScript实现:

//基数排序
var radixSort = function(arr,type,showSort){
	//计算所有数中最大的是几位数
	var getMaxPow = function(out){
		//求所有数中最大的
		var max = 0;
		for(let i=0;i<out.length;i++){
			if(out[i]>max){
				max = out[i];
			}
		}
		//计算所有数中最大的是几位数
		var max_pow = 1;
		while(max>=10){
			max_pow++;
			max = parseInt(max/10);
		}
		return max_pow;
	}
	//升序,分配
	var distribute = function(out, pow){
		var queue = [];
		for(let i=0;i<out.length;i++){
			let m = parseInt(out[i]/pow)%10;
			if(Object.prototype.toString.call(queue[m]) !== "[object Array]"){
				queue[m] = [];
			}
			queue[m].push(out[i]);
		}
		return queue;
	}
	//升序,收集
	var collect = function(queue){
		var out = [];
		for(let i=0;i<10;i++){
			while(queue[i]!==undefined && queue[i].length>0){
				out.push(queue[i].shift());
			}
		}
		return out;
	}
	var max_pow = getMaxPow(arr);
	var queue = [], output = [];
	for(let i=0;i<max_pow;i++){
		queue = distribute(arr,Math.pow(10,i));
		output = collect(queue);
	}
	return output;
}

时间复杂度:

最好: O(d*(n+m)) ,其中d是最大数的位数,例如最大123,则d=3

空间复杂度: O(m)


遍历可视化界面:


遍历可视化完整代码:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>排序</title>
   <!--<link href="index.css" rel="stylesheet" type="text/css"/> -->
   <style type="text/css">
   		.container{
			position: relative;
   			width: 800px;
   			margin: 0 auto;
   		}
		.control{
			margin-bottom: 20px;
		}
		.circle{
			margin: 0 auto;
			width: 50px;
			height: 50px;
		}
		input[type="number"]{
			width: 60px;
		}
		/* 
		rect:hover{
		    fill: #4169E1;
		} */
   </style>
</head>
<body>
<div class="container">
	<div class="control">
		数组长度: 
		<input type="number" id="number">
		<input type="button" name="click" value="随机生成数组" id="getArr">
		<select id="order">
			<option>升序</option>
			<option>降序</option>
		</select>
		<select id="select">
			<option>冒泡排序</option>
			<option>直接插入排序</option>
			<option>希尔排序</option>
			<option>快速排序</option>
			<option>选择排序</option>
			<option>堆排序</option>
			<option>归并排序</option>
			<option>桶排序</option>
			<option>基数排序</option>
		</select>
		动画时间间隔(ms):
		<input type="number" value="1000" id="deley">
		<input type="button" name="click" value="开始排序" id="button">
	</div>
	<svg id="svg" width="800" height="500"></svg>
</div>

<script type="text/javascript">
"use strict";//严格模式
(function(){
var animation_flag = false;//正在执行动画
//计算加入事件队列的时刻,与执行延时函数的次数,相等时表示动画执行完毕
var show_count = 0,deley_count = 0;
var deley_space = 1000;//动画延时间隔
var select = document.getElementById("select");
var button = document.getElementById("button");
var orderSelect = document.getElementById("order");
var getArrDom = document.getElementById("getArr");
var numberDom = document.getElementById("number");
var deleyDom = document.getElementById("deley");

//随机生成指定长度,固定区间的数组
var random = function(n,max,min){
	var arr = [];
	if(typeof max !== "number"||max<100){
		max = 100;
	}
	if(typeof min !== "number"){
		min = 0;
	}
	for(let i=0;i<n;i++){
		arr[i] = parseInt(Math.random()*(max-min+1))+min;
	}
	return arr;
}
//将数组用SVG显示数组
var showArr = function(arr,m,n){
	var svg = document.getElementById("svg");
	var rectStr = "",textStr = "",lineStr = "";//矩形、文本和线的HTML字符串
	var height = svg.getAttribute("height"),width = svg.getAttribute("width");//画布宽高
	var rectWidth = 0, rectHeight = 0, spaceWidth = 0;//矩形宽度、间隔宽度
	var margin_level = 20,margin_veticle = 40;//水平、垂直边距
	var maxValue = 0;
	rectWidth = (width-margin_level*2)/(arr.length)*0.6;
	spaceWidth = rectWidth*2/3;
	svg.innerHTML = "";

	var getMax = function(arr){
		var max = 0;
		for(let i=0;i<arr.length;i++){
			if(max < arr[i]){
				max = arr[i];
			}
		}
		return max;
	}
	var getHeight = function(h){
		return (height-2*margin_veticle)*(h/maxValue);
	}
	maxValue = getMax(arr);
	
	//画线和数字
	for(let i=0;i<arr.length;i++){
		let cx = 0, cy = 0;//当前结点的定位像素坐标
		let color = "#5CACEE";
		rectHeight = getHeight(arr[i]);
		cx = i * (spaceWidth + rectWidth) + margin_level;
		cy = height - rectHeight - margin_veticle;

		if(i===m){
			color = "#FF3E96";
		}else if(i===n){
			color = "#7CCD7C";
		}
		if(i===m&&i===n){
			color = "#EEC900";
		}
		var font_size = rectWidth/2>20?20:rectWidth/2
		rectStr += '<rect x="'+cx+'" y="'+cy+'" width="'+rectWidth+'" height="'+rectHeight+'" fill="'+color+'"/>';
		//lineStr += '<line x1="'+startcx+'" y1="'+startcy+'" x2="'+endcx+
		//	'" y2="'+endcy+'" style="stroke:#999;stroke-width:2" />';

		textStr += '<text x="'+(cx+rectWidth/2)+'" y="'+(cy-6)+'" fill="#171717"  style="font-size:'+font_size+'pt;text-anchor: middle">'+arr[i]+'</text>';
		textStr += '<text x="'+(cx+rectWidth/2)+'" y="'+(height-25+font_size)+'" fill="#171717"  style="font-size:'+font_size+'pt;text-anchor: middle">'+(i+1)+'</text>';
	}
	lineStr += '<line x1="0" y1="0" x2="0" y2="'+(height-30)+'" style="stroke:#666;stroke-width:4" />'
	+'<line x1="0" y1="'+(height-margin_veticle+10)+'" x2="'+width+'" y2="'+(height-margin_veticle+10)+'" style="stroke:#666;stroke-width:2" />';
	svg.innerHTML = lineStr+rectStr+textStr;
}
//延时动画
var showSort = function(arr,m,n){
	var array = [];
	for(let i=0;i<arr.length;i++){
		array[i] = arr[i];
	}
	var callback = function(array,m,n){
		animation_flag = true;
		showArr(array,m,n);
		deley_count++;
		if(deley_count>=show_count){
			setTimeout(function(){
				showArr(array);
				animation_flag = false;//动画执行完毕
				console.log("排序完成");
				show_count=0;
				deley_count=0;
			},deley_space);
		}
	}
	setTimeout(callback,deley_space*show_count++,array,m,n);
}
//各种排序算法
var Sort = function(){
	//判断是否为数组
	var isArray = function(arr){
		if(Object.prototype.toString.call(arr) === "[object Array]"){
			return true;
		}else{
			return false;
		}
	}
	//冒泡排序, arr:Array type:Boolean(true为升序,false为降序),默认为升序,showSort:排序可视化
	var bubbleSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		for(let i=1;i<out.length;i++){
			for(let j=0;j<out.length-i;j++){
				if(typeof showSort === "function"){
					showSort(out,j,j+1);
				}
				if(order*out[j] > order*out[j+1]){
					let tem = out[j];
					out[j] = out[j+1];
					out[j+1] = tem;
				}
				if(typeof showSort === "function"){
					showSort(out,null,null);
				}
			}
		}
		//console.log(out);
		return out;
	}
	//插入排序
	var insertSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		for(let i=1;i<out.length;i++){
			let temp = out[i];
			for(var j=i-1;j>=0;j--){
				if(order*out[j]>order*temp){
					out[j+1] = out[j];
					out[j] = temp;
					if(typeof showSort === "function"){
						showSort(out,j,null);
					}
				}else{
					break;//找到比temp小的则跳出循环
				}
			}
			out[j+1] = temp;//在比temp小的值后面插入temp值
			if(typeof showSort === "function"){
				showSort(out,j+1,j+1);
			}
		}
		return out;
	}
	//希尔排序
	var shellSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		var half = parseInt(out.length/2);
		for(let d=half;d>=1;d=parseInt(d/2) ){
			for(let i=d;i<out.length;i++){
				for(let j=i-d;j>=0;j-=d){
					if(typeof showSort === "function"){
						showSort(out,j,j+d);
					}
					if(order*out[j+d] < order*out[j]){
						let tem = out[j+d];
						out[j+d] = out[j];
						out[j] = tem;
					}
					if(typeof showSort === "function"){
						showSort(out,null,null);
					}
				}
			}
		}
		return out;
	}
	//快速排序
	var quickSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		//快速排序
		var quick = function(out,first,end){
			if(first<end){
				let i=first, j=end, temp=0;
				//一个循环完成一趟扫描
				while(i<j){
					while(i<j&& order*out[i]< order*out[j]){
						if(typeof showSort === "function"){
							showSort(out,i,j);
						}
						j--;
					}
					if(i<j){
						if(typeof showSort === "function"){
							showSort(out,i,j);
						}
						temp = out[i];
						out[i] = out[j];
						out[j] = temp;
						if(typeof showSort === "function"){
							showSort(out,null,null);
						}
						i++;
					}
					while(i<j&& order*out[i]<order*out[j]){
						if(typeof showSort === "function"){
							showSort(out,j,i);
						}
						i++;
					}
					if(i<j){
						if(typeof showSort === "function"){
							showSort(out,j,i);
						}
						temp = out[i];
						out[i] = out[j];
						out[j] = temp;
						if(typeof showSort === "function"){
							showSort(out,null,null);
						}
						j--;
					}
				}
				if(typeof showSort === "function"){
					showSort(out,i,i);
				}
				quick(out,first,i-1);
				quick(out,i+1,end);
			}
			return out;
		}
		
		return quick(out,0,out.length-1);
	}
	//选择排序
	var selectSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		for(let i=0;i<out.length;i++){
			let index = i;
			for(let j=i;j<out.length;j++){
				if(typeof showSort === "function"){
					showSort(out,index,j);
				}
				if(order*out[j]<order*out[index]){
					index = j;
				}
			}
			let temp = out[i];
			out[i] = out[index];
			out[index] = temp;
			if(typeof showSort === "function"){
				showSort(out,null,null);
			}
		}
		
		return out;
	}
	//堆排序
	var heapSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		var len = out.length;
		//建立堆
		var sift = function(out, k, m){
			let i = k, j = 2*k+1;
			while(j <= m && j!=len){
				if(j<m && out[j+1] && order*out[j]<order*out[j+1]){
					j++;
				}
				if(order*out[i] > order*out[j]){
					break;
				}else{
					if(typeof showSort === "function"){
						showSort(out,i,j);
					}
					let temp = out[i];
					out[i] = out[j];
					out[j] = temp;
					if(typeof showSort === "function"){
						showSort(out,null,null);
					}
					i = j;
					j = 2*i+1;
				}
			}
		}
		
		let half = parseInt(len/2);
		//初始建堆
		for(let i=half-1;i>=0;i--){
			sift(out, i, len);
		}
		for(let i=0;i<len-1;i++){
			if(typeof showSort === "function"){
				showSort(out,0,len-1-i);
			}
			let temp = out[0];
			out[0] = out[len-1-i];
			out[len-1-i] = temp;
			if(typeof showSort === "function"){
				showSort(out,null,null);
			}
			sift(out, 0, len-1-i-1);
		}
		
		return out;
	}
	//归并排序
	var mergeSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		var order = type!==true?1:-1;
		//一次归并算法
		var merge = function(out, array, s, m, t){
			let i=s, j=m+1, k=s;//i:左边数组的索引,j:右边数组的索引,k:归并结果数组的索引
			//没有数组遍历完
			while(i<=m && j<=t){
				if(order*out[i]<order*out[j]){
					if(typeof showSort === "function"){
						showSort(array,i,j);
					}
					array[k++] = out[i++];
					if(typeof showSort === "function"){
						showSort(array,k-1,k-1);
					}
				}else{
					if(typeof showSort === "function"){
						showSort(array,i,j);
					}
					array[k++] = out[j++];
					if(typeof showSort === "function"){
						showSort(array,k-1,k-1);
					}
				}
			}
			//如果左数组没有遍历完,将左数组剩余数据压入arr
			if(i<=m){
				while(i<=m){
					if(typeof showSort === "function"){
						showSort(array,null,i);
					}
					array[k++] = out[i++];
					if(typeof showSort === "function"){
						showSort(array,null,null);
					}
				}
			}else{
				while(j<=t){
					if(typeof showSort === "function"){
						showSort(array,null,j);
					}
					array[k++] = out[j++];
					if(typeof showSort === "function"){
						showSort(array,k-1,k-1);
					}
				}
			}
			//console.log(arr);
			return array;
		}
		//一趟归并排序算法
		var mergePass = function(out, array, h){
			var len = out.length;
			let i = 0;
			while(i+2*h<=len){
				merge(out, array, i , i+h-1, i+2*h-1);
				i += 2*h;
			}
			if(i+h<len){
				merge(out, array, i, i+h-1, len-1);
			}else{
				while(i<len){
					array[i] = out[i];
					i++;
				}
			}
			//console.log(arr);
			return array;
		}
		//非递归归并排序
		var mergeSortUnrecursion = function(out){
			var len = out.length;
			var array = [];
			for(let i=0;i<arr.length;i++){
				array[i] = out[i];
			}
			var h = 1;
			while(h<len){
				mergePass(out, array, h);
				h = 2*h;
				mergePass(array, out, h);
				h = 2*h;
			}
			//console.log(out);
			return out;
		}
		//递归归并排序
		var mergeSortRecursion = function(out,array, s, t){
			if(s === t){
				array[s] = out[s];
			}else{
				let m = parseInt((s+t)/2);
				mergeSortRecursion(out, array, s, m);
				mergeSortRecursion(out, array, m+1, t);
				merge(array, out, s, m, t);
				//将out复制给array,继续下一轮归并
				for(let i=0;i<out.length;i++){
					array[i] = out[i];
				}
			}
			return out;
		}
		var array = [];
		return mergeSortUnrecursion(out,array, 0, arr.length-1);
	}
	//桶排序
	var bucketSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		var len = out.length;
		
		var bucket = [], q = [];
		for(let i=0;i<len;i++){
			let m = out[i];
			if(bucket[m] === undefined){
				bucket[m] = 1;
			}else{
				bucket[m]++;
			}
		}
		//out数组索引
		out.splice(0,out.length);
		var index = 0;
		if(type!==true){
			//升序
			for(let i=0;i<bucket.length;i++){
				let temp = bucket[i];
				while(temp>=1){
					out[index++] = i;
					if(typeof showSort === "function"){
						showSort(out,null,null);
					}
					temp--;
				}
			}
		}else{
			//降序
			for(let i=bucket.length-1;i>=0;i--){
				let temp = bucket[i];
				while(temp>=1){
					out[index++] = i;
					if(typeof showSort === "function"){
						showSort(out,null,null);
					}
					temp--;
				}
			}
		}

		
		return out;
	}
	//基数排序
	var radixSort = function(arr,type,showSort){
		//输入检测
		if(!isArray(arr)){
			throw Error("the input of bubbleSort is not a array");
			return;
		}
		//深拷贝,不改动输入数组
		var out = [];
		for(let i=0;i<arr.length;i++){
			out[i] = arr[i];
		}
		//order==false为升序,否则为降序
		var order = type!==true?1:-1;
		var len = out.length;
		//求所有数中最大的
		var max = 0;
		for(let i=0;i<len;i++){
			if(out[i]>max){
				max = out[i];
			}
		}
		//计算所有数中最大的是几位数
		var max_pow = 1;
		while(max>=10){
			max_pow++;
			max = parseInt(max/10);
		}
		//升序,分配
		var distributeUp = function(out, queue, pow){
			queue.splice(0,queue.length);
			for(let i=0;i<len;i++){
				let m = parseInt(out[i]/pow)%10;
				if(Object.prototype.toString.call(queue[m]) !== "[object Array]"){
					queue[m] = [];
				}
				queue[m].push(out[i]);
			}
		}
		//升序,收集
		var collectUp = function(out, queue){
			out.splice(0,out.length);
			for(let i=0;i<10;i++){
				while(queue[i]!==undefined && queue[i].length>0){
					out.push(queue[i].shift());
				}
			}
		}
		//降序,分配
		var distributeDown = function(out, queue, pow){
			queue.splice(0,queue.length);
			for(let i=len-1;i>=0;i--){
				let m = parseInt(out[i]/pow)%10;
				if(Object.prototype.toString.call(queue[m]) !== "[object Array]"){
					queue[m] = [];
				}
				queue[m].push(out[i]);
			}
		}
		//降序,收集
		var collectDown = function(out, queue){
			out.splice(0,out.length);
			for(let i=9;i>=0;i--){
				while(queue[i]!==undefined && queue[i].length>0){
					out.push(queue[i].pop());
				}
			}
		}

		var queue = [];
		if(type!==true){
			//升序
			for(let i=0;i<max_pow;i++){
				distributeUp(out, queue, Math.pow(10,i));
				collectUp(out, queue);
			}
		}else{
			//降序
			for(let i=0;i<max_pow+1;i++){
				distributeDown(out, queue, Math.pow(10,i));
				collectDown(out, queue);
			}
		}
		if(typeof showSort === "function"){
			showSort(out,null,null);
		}
		
		return out;
	}
	var obj = {
		bubbleSort: bubbleSort,
		insertSort: insertSort,
		shellSort: shellSort,
		quickSort: quickSort,
		selectSort: selectSort,
		heapSort: heapSort,
		mergeSort: mergeSort,
		bucketSort: bucketSort,
		radixSort: radixSort
	}
	return obj;
}
//初始化函数
var init = function(){
	//实例化排序对象
	var sort = new Sort();
	var arr = [];
	var number = Number(numberDom.value.trim());
	if(number!==NaN&&number>0){
		arr = random(number,number,1);
		showArr(arr);
	}else{
		arr = random(10,100,1);//随机生成待排序的数组
		//刷新显示初始数组
		showArr(arr);
	}
	arr = [2,8,7,4,1,5];
	console.log("待排序的数组",arr);
	showArr(arr);

	//随机生成指定长度的数组
	getArrDom.addEventListener("click",function(){
		if(animation_flag===true){
			return;
		}
		var number = Number(numberDom.value.trim());
		if(number!==NaN&&number>0){
			arr = random(number,number,1);
			console.log("随机生成的数组:",arr);
			showArr(arr);
		}else{
			alert("请输入正确的数组长度");
		}
	});
	//点击开始排序
	button.addEventListener("click",function(){
		//在动画中点击无效
		if(animation_flag===true){
			return;
		}
		deley_space = Number(deleyDom.value.trim());//输入的动画时间间隔,控制动画快慢(单位ms)
		var order = orderSelect.selectedIndex===0?false:true;//选择的排序顺序
		var index = select.selectedIndex;//选择的排序方法
		var traversalArr = [];
		//按选择的排序方式和顺序排序
		switch(index){
			case 0: traversalArr = sort.bubbleSort(arr,order,showSort);
					console.log("冒泡排序:",traversalArr);
					break;
			case 1: traversalArr = sort.insertSort(arr,order,showSort);
					console.log("插入排序:",traversalArr);
					break;
			case 2: traversalArr = sort.shellSort(arr,order,showSort);
					console.log("希尔排序:",traversalArr);
					break;
			case 3: traversalArr = sort.quickSort(arr,order,showSort);
					console.log("快速排序:",traversalArr);
					break;
			case 4: traversalArr = sort.selectSort(arr,order,showSort);
					console.log("选择排序:",traversalArr);
					break;
			case 5: traversalArr = sort.heapSort(arr,order,showSort);
					console.log("堆排序:",traversalArr);
					break;
			case 6: traversalArr = sort.mergeSort(arr,order,showSort);
					console.log("归并排序:",traversalArr);
					break;
			case 7: traversalArr = sort.bucketSort(arr,order,showSort);
					console.log("桶排序:",traversalArr);
					break;
			case 8: traversalArr = sort.radixSort(arr,order,showSort);
					console.log("基数排序:",traversalArr);
					break;
			default: //alert("选择遍历方式出错");
					break;
		}	
	});
}
//初始化
init();

})();
</script>
</body>
</html>


  • 9
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值