JS写的排序算法演示

看到网上有老外写的,就拿起自已之前完成的jmgraph画图组件也写了一个。
想了解jmgraph的请移步:https://github.com/jiamao/jmgraph

当前演示请查看:http://graph.jm47.com/example/sort.html

排序算法动画


<!doctype html>
<html>
	<head>
		<meta content="text/html; charset=UTF-8" http-equiv="content-type" />
		<title>排序算法演示</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script src="http://mat1.gtimg.com/www/mobi/js/zepto.min.js"></script>
		<!--[if lt IE 9]><script type="text/javascript" src="../../jmgraph/src/lib/excanvas.js"></script><![endif]-->	
		<!--<script type="text/javascript" src="../src/jmgraph.debug.js"></script>	-->
		<script type="text/javascript" src="http://gqq.gtimg.com/fefeding/jmgraph/dist/jmGraph.min.js"></script>
	</head>
	<button id="btn_quick" href="#">快速排序</button>
	<button id="btn_straightinsertion" href="#">直接插入排序</button>
	<button id="btn_shell" href="#">希尔排序</button>
	<button id="btn_simpleselection" href="#">简单选择排序</button>
	<button id="btn_simpleselection2" href="#">二元选择排序</button>
	<button id="btn_bubble" href="#">冒泡排序</button>
	<body style="width:100%;">
		<div id="mycanvas" style="border:1px solid #ddd;width:100%;"></div>		
	</body>
	<script type="text/javascript">

			//排序管理对象
			function jmSort() {
				//原始数组个数
				this.arrCount = 20;
				//原始数组
				this.source = [];

				var container = document.getElementById('mycanvas');
				this.canvasWidth = Math.min(600, document.getElementById('mycanvas').offsetWidth - 10);
				container.style.width = this.canvasWidth + 'px';
				this.canvasHeight = Math.min(450, window.innerHeight - 10);
				container.style.height = this.canvasHeight + 'px';

				this.graph = new jmGraph('mycanvas',this.canvasWidth,this.canvasHeight);				
				this.rectStyle = {
					stroke:'rgb(173,173,209)',
					lineWidth:1,
					close:true,
					fill: 'rgb(8,209,54)'
				};

				this.disableStyle = {
					stroke:'rgba(173,173,209,0.8)',
					lineWidth:1,
					close:true,
					fill: 'rgba(88,196,113,0.5)'
				};
				//this.rectStyle.shadow = this.graph.createShadow(2,2,2,'rgb(39,40,34)');

				this.selectRectStyle = {
					stroke:'rgb(120,20,80)',
					lineWidth:1,
					zIndex: 100,
					close:true,
					fill: 'rgba(255,180,5,0.7)'
				};
				//this.selectRectStyle.shadow = this.graph.createShadow(4,4,6,'rgb(39,40,34)');

				//基准样式
				this.datumRectStyle = {
					stroke:'rgb(224,84,68)',
					lineWidth:2,
					close:true,
					zIndex: 50,
					lineType: 'dotted',
					fill: 'rgba(224,84,68,0.7)'
				};

				this.labelStyle = {
					stroke:'rgb(120,20,80)',
					textAlign: 'center',
					textBaseline: 'middle',
					font: '12px Arial',
					lineWidth:1
				};
			}

			//延迟数组循环
			jmSort.prototype.arrayTimeout = function(arr, fun, endfun, t, index, end) {
				index = index || 0;
				end = end || arr.length;
				var t = t || 200;
				var self = this;
				function intervalArr() {
					if(index >= end) {endfun && endfun(); return;}
					var r = fun(index, arr[index], function(){
						index ++;
						self.timeoutHandler = setTimeout(intervalArr, t);
					});
					if(r === false) {
						endfun && endfun(); return
					}
				}
				intervalArr();
			}

			//柱子移动动画
			jmSort.prototype.rectAnimate = function(rect, cb) {
				if(typeof index == 'function') {
					cb = index;
					index = 0;
				}
				if(!rect.length) {
					rect = [rect];
				}

				var tox = [];
				var offx = [];
				var pos = [];
				var count = rect.length;
				for(var i=0;i<count;i++) {
					pos[i] = rect[i].rect.position();
					//重新计算其x坐标
					tox[i] = this.xStep * rect[i].index + 10;
					offx[i] = (tox[i] - pos[i].x) / 20;
				}
				
				var self = this;
				function move() {
					var complete = 0;
					for(var i=0;i<count;i++) {
						pos[i].x += offx[i];
						if(offx[i] ==0 || (offx[i] > 0 && pos[i].x >= tox[i]) || (offx[i] < 0 && pos[i].x <= tox[i])) {
							pos[i].x = tox[i];	
							complete++;					
						}					
					}
					self.graph.redraw();
					if(complete >= count) {
						cb && cb();
					}
					else {
						self.aniTimeoutHandler = setTimeout(move, 20);
					}					
				}
				move();				
			}

			//播放动画
			jmSort.prototype.play = function(frames, callback) {
				if(typeof frames == 'function') {
					callback = frames;
					frames = null;
				}
				frames = frames || this.frames;
				
				if(frames.length == 0) {
					callback && callback();
					return;
				}
				var f = frames.splice(0,1)[0];//取最早的一个
				var self = this;
				if(f.move && f.move.length) {
					//var count = 0;
					//for(var i=0;i<f.move.length;i++) {
						this.rectAnimate(f.move, function(){
							//count ++;
							//if(count >= f.move.length) {
								self.play(callback);
							//}
						});
					//}
				}
				else if(f.sels) {
					this.selectRect.apply(this, f.sels);
					this.play(callback);
					//setTimeout(function(){
					//	self.play(callback);
					//}, 40);
				}
				else if(f.datum) {
					if(this.datumLine) {
						var start = this.datumLine.start();
						var end = this.datumLine.end();
						start.y = end.y = f.datum.rect.position().y;
						this.datumLine.visible = true;
					}
					this.play(callback);
				}
				else if(f.refresh) {
					this.refreshGraph(f.refresh);
					this.play(callback);
				}	
				else {
					this.play(callback);
				}			
			}

			//初始化排序条件,原始数组
			jmSort.prototype.init = function(){
				this.source = [];
				this.frames = [];
				var max = 100;
				var offy = this.canvasHeight - 20;
				this.graph.children.clear();
				//当前 x轴分布单位宽度
				this.xStep = (this.canvasWidth - 20) / this.arrCount;

				for(var i=0;i<this.arrCount;i++) {
					var v = {};
					v.value = Math.floor(Math.random() * max);
					v.height = v.value / max * offy;
					this.source.push(v);
				}
				//画基准线
				this.datumLine = this.graph.createLine({x:0,y:0},{x:this.canvasWidth,y:0},this.datumRectStyle);
				this.datumLine.visible = false;
				this.graph.children.add(this.datumLine);
				this.refreshGraph(this.source);
			}
			
			jmSort.prototype.reset = function() {
				if(this.timeoutHandler) clearTimeout(this.timeoutHandler);
				if(this.aniTimeoutHandler) 	clearTimeout(this.aniTimeoutHandler);
				if(this.datumLine) this.datumLine.visible = false;
				//this.refreshGraph();	
				this.init();		
			}

			//刷新画布
			jmSort.prototype.refreshGraph = function(arr) {
				arr = arr || this.source;
				//this.graph.children.clear();
				var offy = this.canvasHeight - 20;
				for(var i=0;i<arr.length;i++) {	
					if(arr[i].rect) {
						var pos = arr[i].rect.position();
						pos.x = this.xStep * i + 10;
					}
					else {	
						var pos = {};
						pos.x = this.xStep * i + 10;
						pos.y = offy - arr[i].height;
						arr[i].rect = this.graph.createShape('rect',{position:pos,width: 10, height: arr[i].height, style: this.rectStyle});
						
						var label = this.graph.createShape('label',{style:this.labelStyle,position:{x:0,y:arr[i].height},value:arr[i].value,width:10,height:20});
						
						arr[i].rect.children.add(label);
						this.graph.children.add(arr[i].rect);
					}
					//this.graph.children.add(arr[i].rect);
				}
				this.graph.redraw();
			}

			//选中某几个值,则加亮显示
			jmSort.prototype.selectRect = function(datum, sels, area) {
				var self = this;
				this.graph.children.each(function(i, rect) {
					if(!rect.is('jmRect')) return;
					if(sels && sels.indexOf(rect) > -1) {
						rect.style = self.selectRectStyle;
					}
					else if(datum && datum.indexOf(rect) > -1) {
						rect.style = self.datumRectStyle;
					}
					else if(area && area.indexOf(rect) > -1) {
						rect.style = self.rectStyle;
					}					
					else {
						rect.style = self.disableStyle;
					}
				});
				this.graph.refresh();
			}

			//快速排序
			//取其中一个值,把小于此值的放到其左边,大于此值的放到其右边
			//如此递归
			jmSort.prototype.quickSort = function(arr, callback) {
				if(typeof arr == 'function') {
					callback = arr;
					arr = null;
				}
				arr = arr || this.source;
				
				var self = this;
				function sort(source, oldleft, oldrigth) {
					if(source.length <= 1) return source;
					//取一个值做为比较对象,这里直接取中间的值(任务一个都可)
					var mindex = Math.floor(source.length /2);				
					var m = source[mindex];//基准值
					self.frames.push({datum: m});

					//选中当前区域
					var area = [];
					for(var i=0;i<source.length;i++) {
						area.push(source[i].rect);
					}
					self.frames.push({sels:[[m.rect],null, area]});

					var left = mindex>0?source.slice(0, mindex):[];
					var right = mindex>0&&mindex<source.length-1?source.slice(mindex+1):[];
					var middle = [m];

					var index = oldleft?oldleft.length:0;
					for(var i=0;i<source.length;i++) {
						var s = source[i];
						self.frames.push({sels:[[m.rect],[s.rect], area]});
						var f = {move:[]};
						var sindex = i;
						if(s.value < m.value) {
							if(i < mindex) continue;
							left.push(s);
							var rindex = right.indexOf(s);
							right.splice(rindex, 1);
							sindex = left.length - 1;
							f.move.push({
								rect: s.rect,
								index: sindex + index
							});
							var movearr = middle.concat(right);
							for(var j=0;j<rindex+middle.length;j++) {
								f.move.push({
									rect: movearr[j].rect,
									index: sindex + index + j + 1
								});
							}
						}
						else if(s.value > m.value) {
							if(i > mindex) continue;
							var lindex = left.indexOf(s);							
							left.splice(lindex, 1);
							right.unshift(s);
							sindex = left.length + middle.length;
							f.move.push({
								rect: s.rect,
								index: sindex + index
							});
							var movearr = left.concat(middle);
							for(var j=lindex;j<movearr.length;j++) {								
								f.move.push({
									rect: movearr[j].rect,
									index: index + j
								});
							}
						}
						else if(i != mindex) {
							if(i < mindex) {
								var lindex = left.indexOf(s);
								left.splice(lindex, 1);
								middle.unshift(s);
								sindex = left.length;
								f.move.push({
									rect: s.rect,
									index: sindex + index
								});

								for(var j=lindex;j<left.length;j++) {
									f.move.push({
										rect: left[j].rect,
										index: index + j
									});
								}
							}
							if(i > mindex) {
								var rindex = right.indexOf(s);
								right.splice(right.indexOf(s), 1);
								middle.push(s);
								sindex = left.length + middle.length - 1;
								f.move.push({
									rect: s.rect,
									index: sindex + index
								});
								for(var j=0;j<rindex;j++) {
									f.move.push({
										rect: right[j].rect,
										index: sindex + index + j + 1
									});
								}
							}							
						}
						if(f.move.length) self.frames.push(f);
					}

					return sort(left, oldleft, middle.concat(right, oldrigth||[])).concat(middle, sort(right, (oldleft||[]).concat(left, middle)));
				}				
				var result = sort(arr);				
				this.play(function(){
					callback && callback(result);
				});
				return result;
			}

			//直接插入排序
			//将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
			jmSort.prototype.straightInsertionSort = function(arr, dk, callback) {
				if(typeof arr == 'function') {
					callback = arr;
					arr= null;
				}
				if(typeof dk == 'function') {
					callback = dk;
					dk= 0;
				}

				arr = arr || this.source;
				dk = dk || 1;
				//拷贝一份
				var result = arr.slice(0);
				var self = this;

				for(var i=dk;i<result.length;i++) {
					var k = i;
					var j = k - dk;
					while(j>=0) {
						var v = result[k];
						var pre = result[j];

						this.frames.push({sels: [[v.rect], [pre.rect]]});

						if(v.value < pre.value) {
							result[j] = v;
							result[k] = pre;

							v.index = j;
							pre.index = k;

							this.frames.push({move: [{rect: pre.rect,index:k}, {rect:v.rect,index:j}]});

							k = j;
							j -= dk;
						}
						else {
							break;
						}
					}					
				}

				callback && callback(result);
				return result;				
			}

			//希尔排序
			//先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
			jmSort.prototype.shellSort = function(arr, callback) {
				if(typeof arr == 'function') {
					callback = arr;
					arr = null;
				}
				arr = arr || this.source;
				var dk = Math.floor(arr.length / 2);
				var result = arr;
				while(dk >= 1) {
					result = this.straightInsertionSort(result, dk);
					dk = Math.floor(dk / 2);
				}
				
				this.play(function(){
					callback && callback(result);
				});
				return result;
			}

			//简单选择排序
			//在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
			jmSort.prototype.simpleSelectionSort = function(arr, callback) {
				if(typeof arr == 'function') {
					callback = arr;
					arr = null;
				}
				arr = arr || this.source.slice(0);

				for(var i=0;i<arr.length-1;i++) {
					var min = arr[i];
					var minindex = i;

					for(var j=i+1;j<arr.length;j++) {
						if(min.value > arr[j].value) {
							min = arr[j];
							minindex = j;
						}
						//this.frames.push({sels:[[min.rect], [arr[j].rect]]});
					}

					if(minindex != i) {
						this.frames.push({sels:[[min.rect], [arr[i].rect]]});
						this.frames.push({move: [{rect: min.rect, index: i}, {rect: arr[i].rect, index: minindex}]});
						arr[minindex] = arr[i];
						arr[i] = min;
					}					
				}

				this.play(function(){
					callback && callback(arr);
				});
				
				return arr;
			}

			//二元选择排序
			//简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可
			jmSort.prototype.selection2Sort = function(arr, callback) {
				if(typeof arr == 'function') {
					callback = arr;
					arr = null;
				}
				arr = arr || this.source.slice(0);

				var index = -1;
				var self = this;
				var end = Math.floor(arr.length / 2);
				for(var i=0;i<end;i++) {
					//取最小值和最大值
					var min = arr[i];
					var max = arr[i];
					var minindex = i;
					var maxindex = i;

					for(var j=i+1;j<arr.length-i;j++) {
						if(min.value > arr[j].value) {
							min = arr[j];
							minindex = j;
						}
						if(max.value <= arr[j].value) {
							max = arr[j];
							maxindex = j;
						}
					}

					var maxpos = j - 1;
					this.frames.push({sels:[[min.rect, arr[i].rect], [max.rect, arr[maxpos].rect]]});
					if(minindex != i) {						
						this.frames.push({move: [{rect: min.rect, index: i}, {rect: arr[i].rect, index: minindex}]});
						arr[minindex] = arr[i];
						arr[i] = min;
						//如果最大值是当前起始值,则它被换到最小值位置上了
						//需要重新改变最大值的索引为找到的最小值的索引
						if(maxindex == i) {
							maxindex = minindex;
						}
					}
					if(maxindex != maxpos) {						
						this.frames.push({move: [{rect: max.rect, index: maxpos}, {rect: arr[maxpos].rect, index: maxindex}]});
						arr[maxindex] = arr[maxpos];
						arr[maxpos] = min;
					}		
				}

				this.play(function(){
					callback && callback(arr);
				});
				
				return arr;
			}

			//冒泡排序
			//在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
			jmSort.prototype.bubbleSort = function(arr, callback) {
				if(typeof arr == 'function') {
					callback = arr;
					arr = null;
				}
				arr = arr || this.source.slice(0);

				var i = arr.length - 1;
				while(i > 0) {
					var pos = 0;
					for(var j=0;j<i;j++) {
						this.frames.push({
							sels: [[arr[j].rect],[arr[j+1].rect]]
						});

						if(arr[j].value > arr[j+1].value) {
							pos = j;
							var tmp=arr[j];arr[j]=arr[j+1];arr[j+1]=tmp;
							this.frames.push({
								move: [{
									rect: tmp.rect,
									index: arr.indexOf(tmp)
								},{
									rect: arr[j].rect,
									index: arr.indexOf(arr[j])
								}]
							});
						}
					}
					i=pos;
				}
				this.play(function(){
					callback && callback(arr);
				});
			}

			$(function(){
				//开始
				var sort = new jmSort();
				sort.init();
				sort.selection2Sort(function(ret){
					console.log(ret);
				});		

				$('#btn_quick').click(function(){
					sort.reset();
					sort.quickSort(null,null,null,function(ret) {
						this.datumLine.visible = false;
						this.selectRect(ret);
						console.log(ret);
					});	
					return false;
				});
				$('#btn_straightinsertion').click(function(){
					sort.reset();
					sort.straightInsertionSort(function(result){
						sort.play();
						console.log(result);
					});
					return false;	
				});
				$('#btn_shell').click(function(){
					sort.reset();
					sort.shellSort();
					return false;	
				});
				$('#btn_simpleselection').click(function(){
					sort.reset();
					sort.simpleSelectionSort();	
					return false;
				});
				$('#btn_simpleselection2').click(function(){
					sort.reset();
					sort.selection2Sort();
					return false;				
				});
				$('#btn_bubble').click(function(){
					sort.reset();
					sort.bubbleSort();
					return false;
				});
			});
			
	</script>
</html>



 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
超强的Web在线矢量绘图器与监控系统 http://www.visual-graph.com/article/view.asp?id=1 工业图形网站:http://www.visual-graph.com 图形相关下载:http://www.visual-graph.com/down 图形相关技术:http://www.visual-graph.com/article 图形系统截图:http://www.visual-graph.com/gallery OnlineDraw是一个VG专为IE设计的矢量绘图组件。网页中有了VG,用户就可以直接在网页中绘制矢量图形、处理点阵图片,同时也可以做为一个可视化的图片上传客户端工具。OnlineDraw是一个标准的 COM控件产品,它本身只是一个"白板". 超强的Web在线矢量绘图器 OnlineDraw是一个VG专为IE设计的矢量绘图组件。网页中有了VG,用户就可以直接在网页中绘制矢量图形、处理点阵图片,同时也可以做为一个可视化的图片上传客户端工具。OnlineDraw是一个标准的 COM控件产品,它本身只是一个"白板",您可以根据您的具体需求,对他的所有工具条,图库列表,属性列表框等所有界面进行定制,定制的过程就是一个绘图的过程,所有的工具按钮和绘图事件都可以用 HTML 和 &#106avascript 来创建和激发。 简单几条语句即可在网站上实现功能拓展 控件本身是一个平台,全部功能均由VG内部,JS易于配合WB/S系统的图形绘制服务进行少量(二三十行代码)开发就可以出来了。完全的客户端控件,不增加服务器压力,一切功能均在客户端实现,无需服务器进行实时的数据计算,如果需要服务器的交互,VG也提供相应的接口与方法来在客户端与服务器端进行轻量级XML交互数据。 基于Visual Graph的OnlineDraw图形编辑工具经过近多年的深入研究与应用,有预见性地开发了大量具有潜在应用的功能,长期与用户互相合作,大量吸取用户应用经验,多年来的应用研究试验,Visual Graph终于被打造成图形应用领域里面的超级引擎,其中的OnlineDraw图形编辑器在Web上应用非常广泛,服务不断前进,在技术与质量上赢得了众多合作伙伴的心。OnlineDraw图形编辑器主要特点是: 1.包含编辑与运行状态,可以任意操纵与控制图形变化,实现各种仿真与建模。 2.图形和图库是统一的XML格式,便于传输、存储和维护,支持WEB开发。 3.响应众多的图形操作事件,任意给图形添加属性成函数,任意自定义图形。 4.可以建立和分析图形间的任意拓朴连接关系,实现各种逻辑运算与控制。 5.内含面向对象的Visual Graph脚本语言,简洁易用的COM类库接口。 6.每个图形都是对象,拥有众多的属性和函数,可以方便地编辑和操纵。 7.支持报表开发,表格高度智能,可以制作超酷图形界面,轻松换肤。 8.非常容易地开发出任何复杂变化的图形,轻松实现自己的图形库。 9.技术完善:多页面、多图层、表格图形混排、透明度、自由旋转、动画效果、自定义线型、网纹、箭头、自定义任意多个文字标注、自定义图元连接点、自定义光标、图形保护、控制点,支持图形的逻辑分组,允许图元嵌套构建更复杂的图元。 10.混合编程:不仅其他程序可以操控Visual Graph的一切,而且Visual Graph的脚本可以调用其他语言的程序,也可以调用DLL,任意扩充内部函数,扩展功能。脚本语言的编程习惯和一般语言相同,各种属性、函数名称也都相同。 尺寸小、速度快,运行稳定,能适应工业上机器常年工作等环境;能轻松作出工业上的各种动作设备以及常用的曲线图、棒图、尺寸线,实现工业的过程监控;能让软件公司在短时间内开发出高质量的组态监控软件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值