基于JavaScript实现的八种排序算法的可视化实现
Posted 乘凉者 栽树人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于JavaScript实现的八种排序算法的可视化实现相关的知识,希望对你有一定的参考价值。
八大排序算法 可视化过程
1、效果图
排序过程中,对应比较交换的颜色矩形发生变化
排序前的一段简单介绍
2、八大排序算法核心代码
参考文章:https://blog.csdn.net/qq_23994787/article/details/77965750?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162561910016780264088695%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=162561910016780264088695&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-2-77965750.first_rank_v2_pc_rank_v29&utm_term=java%E6%8E%92%E5%BA%8F&spm=1018.2226.3001.4187
2.1、冒泡排序
-
实现步骤
alert("冒泡排序\\n"+ "1将序列中所有元素两两比较,将最大的放在最后面。\\n" + "2将剩余序列中所有元素两两比较,将最大的放在最后面。\\n" + "3重复第二步,直到只剩下一个数。");//排序前的一段介绍
-
代码实现
for(let i=1;i<out.length;i++) for(let j=0;j<out.length-i;j++) if(typeof showSort === "function")//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;
2.2、直接插入排序
-
实现步骤
alert("直接插入排序\\n" + "1将第一个数和第二个数排序,然后构成一个有序序列\\n" + "2将第三个数插入进去,构成一个新的有序序列。\\n" + "3对第四个数、第五个数……直到最后一个数,重复第二步。");
-
代码实现
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);
2.3、希尔排序
-
实现步骤
alert("希尔排序\\n"+ "1将数的个数设为n,取奇数k=n/2,将下标差值为k的书分为一组,构成有序序列。\\n" + "2再取k=k/2 ,将下标差值为k的书分为一组,构成有序序列。\\n" + "3重复第二步,直到k=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);
2.4、快速排序
-
实现步骤
alert("快速排序\\n"+ "1选择第一个数为p,小于p的数放在左边,大于p的数放在右边。\\n" + "2递归的将p左边和右边的数都按照第一步进行,直到不能递归。")
-
实现代码
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);
2.5、选择排序
-
实现步骤
alert("选择排序\\n"+ "1遍历整个序列,将最小的数放在最前面。\\n" + "2遍历剩下的序列,将最小的数放在最前面。\\n" + "3重复第二步,直到只剩下一个数。");
-
代码实现
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);
2.6、堆排序
-
实现步骤
alert("堆排序\\n" +"1将序列构建成大顶堆。\\n" + "2将根节点与最后一个节点交换,然后断开最后一个节点。\\n" + "3重复第一、二步,直到所有节点断开。")
-
代码实现
//建立堆 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);
2.7、归并排序
-
实现步骤
alert("归并排序\\n"+"1选择相邻两个数组成一个有序序列。\\n" + "2选择相邻的两个有序序列组成一个有序序列。\\n" + "3重复第二步,直到全部组成一个有序序列。");
-
代码实现
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++;
2.8、基数排序
-
实现步骤
alert("基数排序\\n"+ "1将所有的数的个位数取出,按照个位数进行排序,构成一个序列。\\n" + "2将新构成的所有的数的十位数取出,按照十位数进行排序,构成一个序列。")
-
代码实现
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);
3、画一个整体的模型框架
-
随机生成数组的输入框
数组长度: <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> </select>
-
动画播放速度
动画时间间隔(ms): <!--默认排序时间--> <input type="number" value="1000" id="deley"> <input type="button" name="click" value="开始排序" id="button"> </div>
-
在下方定义一个输出画像的画布
</div> <!--定一个图像的输出框架--> <svg id="svg" width="800" height="500"></svg> </div>
-
引入音频
<!--引入音频--> <audio id="myaudio" src="12220.wav" autoplay="autoplay" controls="controls" loop="loop" preload="preload"> </audio>
4、监听事件
-
所有按钮的事件节点
//定义表单事件,document节点对象 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)//处理数字异常以及随机数最大值不超过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;//parseInt() 函数可解析一个字符串,并返回一个整数。 return arr;
-
定义画布的各个属性值
var svg = document.getElementById("svg"); var rectStr = "",textStr = "",lineStr = "";//矩形、文本和线的html字符串 var height = svg.getAttribute("height"),width = svg.getAttribute("width");//画布宽高 var rectWidth, spaceWidth, rectHeight = 0;//矩形宽度、间隔宽度、矩形高度 var margin_level = 20,margin_veticle = 40;//水平、垂直边距 var maxValue = 0;
-
画布显示思路
//画布的显示思路 //1.算出每一个矩形的宽度 //2.用总宽减两个边距,除去数组的总长度,0.6是缩小矩形宽度 //3.考虑到所有矩形宽以及间隙宽的总和不能大于总画布宽度 //4.将间隙宽设定为比边距框更小 rectWidth = (width-margin_level*2)/(arr.length)*0.6; spaceWidth = rectWidth*2/3;
-
每一块矩形高度计算
//取出数组中的最大值 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) //1.防止高度超过画布的高度,(height-2*margin_veticle)在上边距以及下边距预留位置 //2.计算出每一个数组占据这个剩余画布高度的比例(h/maxValue) return (height-2*margin_veticle)*(h/maxValue); maxValue = getMax(arr);//将其最大值赋予maxValue
-
画出矩形 并用innerHTML识别HTML标签
rectStr += '<rect x="'+cx+'" y="'+cy+'" width="'+rectWidth+'" height="'+rectHeight+'" fill="'+color+'"/>';//此处就是将矩形画出,fill的用法路径到开始的点结束,能够自动填充里面的颜色 //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>'; //此处为每一个数字的排序,分别是1,2,3,4,5,。。 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>'; // fill() 方法会从路径结束点到开始点之间添加一条线 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;//innerHTML用法是将内容用HTML的格式编译出的东西赋值,并显示于页面
-
延时动画设计
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("排序完成");//控制台输出 alert("排序完成");//弹窗输出 document.getElementById("myaudio").pause(); show_count=0;//加入事件队列的时刻 deley_count=0;//执行延时函数的次数 ,deley_space); setTimeout(callback,deley_space*show_count++,array,m,n);//setTimeout设置延时,callback设置回调
5、初始化状态
-
实例化对象
var sort = new Sort();//创建算法对象 var arr = []; var number = Number(numberDom.value.trim());//输入的数组值数量 if(number!==NaN&&number>0)//NaN非定义或者不可表示的值 arr = random(number,number,1);//第一个number 传的是数量 第二个传的是最大值 第三为最小值 showArr(arr); else arr = random(10,100,1);//随机生成待排序的数组 showArr(arr);//刷新显示初始数组 arr = [1,4,3,3,2,2,3]; console.log("待排序的数组",arr); showArr(arr);
-
监听生成指定长度数组
getArrDom.addEventListener("click",function()//addEventListener监听事件 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("请输入正确的数组长度"); );
-
点击排序
//按选择的排序方式和顺序排序 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;
6、小结
-
注意在数组传递的时候,用到
-
3个=和2个=的区别
//===:先比较数据类型,再比较值,如果数据类型不匹配,返回false,即"严格等"的运算符 //==:先比较数据类型,再比较值,如果数据源类型不匹配,则会将其数据类型转为同一数据类型,再比较
-
赋值、浅拷贝和深拷贝的区别
//赋值,原数据指向同一对象,数据类型改变会使得原数据一起改变,子对象改变会使原数据改变 //浅拷贝,原数据不指向同一对象,数据类型改变不会使得原数据一起改变,子对象改变会使原数据改变 // 深拷贝,不和元素据一样,不改动输入数组
-
有兴趣的朋友可以尝试每一条矩形颜色都不一样,加更多的交换声音
let color=randomColor(); function randomColor() //得到随机的颜色值 var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return "rgb(" + r + "," + g + "," + b + ")";
以上是关于基于JavaScript实现的八种排序算法的可视化实现的主要内容,如果未能解决你的问题,请参考以下文章