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="../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>

 

posted on 2015-07-27 09:42  家猫47  阅读(705)  评论(0编辑  收藏  举报

导航