基于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实现的八种排序算法的可视化实现的主要内容,如果未能解决你的问题,请参考以下文章

基于JavaScript实现的八种排序算法的可视化实现

基于JavaScript实现的八种排序算法的可视化实现

Java常用的八种排序算法与代码实现

Java常用的八种排序算法与代码实现

Java常用的八种排序算法与代码实现

Java常用的八种排序算法与代码实现