zoukankan      html  css  js  c++  java
  • 复习排序with javascript

    最近又翻看了一下数据结构(数据结构学渣)。

    以前总是看不懂,连冒泡和选择排序都要纠结半天,后来才慢慢有意识能区分开来。

    当真的理解了快速排序之后,才觉得,这是个很赞的排序,很容易理解。

    于是简单的,模仿c的做法,实现了javascript上的排序,目前只有冒泡、选择和快速排序。//不过貌似快速排序用到了传递的性质,也许我应该改改。

    function bubbleSort(arr){
        console.log("冒泡排序:");
        var len = arr.length;
        for(var i=0; i<len-1;i++){
            for(var j=0; j<len-i-1; j++){
                if(arr[j]>arr[j+1]){
                    var temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        return arr;
    }
    function chooseSort(arr){
        console.log("选择排序:");
        var len = arr.length;
        for(var i=0; i<len-1;i++){
            for(var j=i+1; j<len; j++){
                if(arr[i]>arr[j]){
                    var temp = arr[j];
                    arr[j] = arr[i];
                    arr[i] = temp;    
                }
            }
        }
        return arr;
    }
    function quickSort(arr, left, right){
        if(undefined===left){
            console.log("快速排序改实参版:");
            left=0;
        }
        if(undefined===right){
            right = arr.length-1;
        }
        if(left>right){
            return;
        }
        //移动左右索引变量
        var i = left;
        var j = right;
        var midKey = arr[left];
        while(i<j){
            while(i<j&&midKey<=arr[j]){
                j--;
            }
            while(i<j&&midKey>=arr[i]){
                i++;
            }
            if(i<j){
                var temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        //交换中间数与中轴数
        arr[left] = arr[i];
        arr[i] = midKey;
        //递归左右,分治思想
        quickSort(arr, left, i-1);
        quickSort(arr, i+1, right);
        return arr;
    }
    function quickSortArr(arr){
        if(arr.length==1){
            return arr;
        }
        if(arr.length==0){
            return [];
        }
        var left = [],
            right = [];
        var povit = arr[0];
        for(var i=1;i<arr.length;i++){
            if(arr[i]<=povit){
                left.push(arr[i]);
            }else{
                right.push(arr[i]);
            }
        }
        var re = quickSortArr(left).concat([povit],quickSortArr(right));
        return re;
    }
    function timeIt(fn, arr){
        var start = new Date().getTime();
        arr = fn(arr);
        var end = new Date().getTime();
        console.log("cost(ms):"    +(end-start)+" result:"+arr[0]+ " " + arr[1] +" "+arr[2]+"..."+arr[arr.length-1]);
    }
    function callArraySort(arr){
        console.log("Array内置排序:");
        //console.log(arr[0]);
        return Array.prototype.sort.call(arr, compareByArray);
        //console.log(b[0]);
        //console.log(b.length);
    }
    function compareByArray(a,b){
        var i = parseInt(a);
        var j = parseInt(b);
        return i-j;
    }
    
    
    function heapSort(arr){
        function maxHeapify(arr, index, len){
            var left = getLeft(index);
            var right = getRight(index);
            var largest;
            if(left<len&&arr[left]>arr[index]){
                largest = left;
            }else{
                largest = index;
            }
            if(right<len&&arr[right]>arr[largest]){
                largest = right;
            }
            if(largest!=index){
                swapElement(arr, largest, index);
                maxHeapify(arr, largest, len); // 如果改变了位置,递归改变的子树调整
            }
        }
        function swapElement(arr,index0, index1){
            var temp = arr[index0];
            arr[index0] = arr[index1];
            arr[index1] = temp;
        }
        function getLeft(eleIndex){
            return 2*eleIndex+1;
        }
        function getRight(eleIndex){
            return 2*(eleIndex+1);
        }
        if(arr.length<=1){
            return;
        }
        var len = arr.length;
        for(var i=len/2+1;i>=0;--i){
            maxHeapify(arr, i, len);
        }
    
        for(var i=len-1;i>=1;--i){
            swapElement(arr, 0, i);
            maxHeapify(arr, 0, --len);
        }
        return arr;
    }
    var a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7];
    //console.log(bubbleSort(a));
    timeIt(bubbleSort, a);
    a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7];
    timeIt(chooseSort, a);//console.log(chooseSort(a));
    a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7];
    timeIt(quickSort, a);//console.log(quickSort(a));
    a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7];
    console.log("快速排序不改原参数版:");
    timeIt(quickSortArr, a);
    a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7];
    timeIt(callArraySort, a);
    a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7];
    console.log("堆排序:");
    timeIt(heapSort, a);
    var randArr = [];
    for(var i=0;i<10000; i++){
        randArr[i] = Math.floor(Math.random()*100000);
    } 
    a = randArr.slice();
    timeIt(bubbleSort, a)
    a = randArr.slice();
    timeIt(chooseSort, a);//console.log(chooseSort(a));
    a = randArr.slice();
    timeIt(quickSort, a);
    a = randArr.slice();
    console.log("快速排序不改原参数版:");
    timeIt(quickSortArr, a);
    a = randArr.slice();
    timeIt(callArraySort, a);
    a = randArr.slice();
    console.log("堆排序:");
    timeIt(heapSort, a);

    冒泡跟选择以前容易混淆,是因为不明白怎样叫冒泡,实际上,冒泡就是对整个序列,进行一趟前后元素两两比较的形式,大的数后移(或者小的),这样达到一趟实现了把大的数(或者小的数)移到了尾部,那整一个序列看成尾部朝上,往后一趟序列,从头开始一直到倒数第n个数进行两两比较,犹如关键元素(最大值或者最小值)往上冒泡的样子 ,因此叫冒泡。

    而选择排序,则是对序列的每个数,从头开始,针对该位置与剩下的数进行比较, 如果有出现大于(小于)该位置的数,则交换位置,这样每一趟下来,就能确定固定位置该放置的数。 //这个解释感觉跟选择没多大关联,姑且当做是选择位置吧。

    那么快速排序,就是直接用效率命名了。为什么快呢?看代码实现 ,while里面还有while,还有递归,感觉不快的样子,关于时间复杂度的问题,我还没嚼透。

    快排思想是分而治之,既然一个分散的序列,假设通过比较某个值,分别将大于和小于该数的分到一边,就成了两部分,然后同样的道理,各部分再按切分大小方法再细分成两部分……以此推下去,直到变成了最小的部分就是三个数,a<b<c,这个时候各个细小部分组合起来,明显就是一个排序好的序列了。

    最后贴一下代码在chrome console运行的结果,快排最后秒杀全场

     

    2014-06-20 更新: 快排加入另一种实现,利用array的api,不过似乎速度不尽如人意,但是做到了不破坏实参数组的效果,而且不过在元素大的时候还是比冒泡选择强:

     1 function quickSortArr(arr){
     2     if(arr.length==1){
     3         return arr;
     4     }
     5     if(arr.length==0){//这两句if实际上可以写成一句的
     6         return [];
     7     }
     8     var left = [],
     9         right = [];
    10     var povit = arr[0];
    11     for(var i=1;i<arr.length;i++){
    12         if(arr[i]<=povit){
    13             left.push(arr[i]);
    14         }else{
    15             right.push(arr[i]);
    16         }
    17     }
    18     var re = quickSortArr(left).concat([povit],quickSortArr(right));
    19     return re;//这里为了调试才加的变量,实际可以不用
    20 }

    测试的时候,没留意到不改变原数组的问题,打印出原数组 ,我一度以为是自己写错,囧。好吧,too simple。

    又更新,傻了,居然没把Array内置排序也加进去玩玩,不过出现点小插曲,就是sort方法的参数,默认是按元素的编码比较,单位数比较可能没发觉,但是多位数比较就会发现,不设置比较函数的话,10是小于2的,事关10的1确实小于2 = =||

    没关系,我们再加个比较函数进去就可以了,如下:(整体代码更新到第一个代码区了。)

    function callArraySort(arr){
         console.log("Array内置排序:");
        return Array.prototype.sort.call(arr, compareByArray);
     
    }
    function compareByArray(a,b){//其实直接return a-b;应该也可以。
         var i = parseInt(a);
         var j = parseInt(b);
        return i-j;
     }

    一开始没留意,后来查了mdn才意识到。MDN入口>>

    贴一下排序结果:似乎内置排序比实际快排快,但比改实参版的慢哦。

     新更新,加入了堆排序,实现是参考的这里>>

     1 function heapSort(arr){
     2     function maxHeapify(arr, index, len){//对以arr[index]为根的子树的最大堆生成,如果交换了父节点,就再整理被交换的子节点
     3         var left = getLeft(index);
     4         var right = getRight(index);
     5         var largest;
     6         if(left<len&&arr[left]>arr[index]){
     7             largest = left;
     8         }else{
     9             largest = index;
    10         }
    11         if(right<len&&arr[right]>arr[largest]){
    12             largest = right;
    13         }
    14         if(largest!=index){
    15             swapElement(arr, largest, index);
    16             maxHeapify(arr, largest, len); // 如果改变了位置,递归改变的子树调整
    17         }
    18     }
    19     function swapElement(arr,index0, index1){//交换位置
    20         var temp = arr[index0];
    21         arr[index0] = arr[index1];
    22         arr[index1] = temp;
    23     }
    24     function getLeft(eleIndex){//获取eleIndex节点的左子节点
    25         return 2*eleIndex+1;
    26     }
    27     function getRight(eleIndex){//获取eleIndex节点的右子节点 以0为起始下标
    28         return 2*(eleIndex+1);
    29     }
    30     if(arr.length<=1){//排序起始,只有一个或者没有的数组,本来就不需要排序
    31         return arr;
    32     }
    33     var len = arr.length;
    34     for(var i=len/2+1;i>=0;--i){//首先是对无序的数组进行堆顶树的构造;len/2 + 1,我认为取的是树的最后一层的第一个叶子节点,后续叶子会在其父节点时比较 
    35         maxHeapify(arr, i, len);
    36     }
    37 
    38     for(var i=len-1;i>=1;--i){
    39         swapElement(arr, 0, i);
    40         maxHeapify(arr, 0, --len);
    41     }
    42     return arr;
    43 }

    看了下,堆排序貌似还比内置排序快一点,比快排轻版块的慢一点:

  • 相关阅读:
    2804 最大最小数质因数
    5429 多重背包
    1851 越狱
    Gvim使用
    3622 假期
    4906 删数问题
    2845 排序的代价
    poj 3352
    常用正则表达式汇总
    功能简单例子
  • 原文地址:https://www.cnblogs.com/dont27/p/3796312.html
Copyright © 2011-2022 走看看