各种排序方法的JS实现

各种排序算法的对比总结如下表所示:

冒泡排序:

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
冒泡排序算法的运作如下:

比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,到最后一对比较完之后最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:C_{min}=n-1,M_{min}=0。

所以,冒泡排序最好的时间复杂度为O(n),冒泡排序的最坏时间复杂度为O(n^2)。综上,因此冒泡排序总的平均时间复杂度为O(n^2)。
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,
我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,
所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

function bubbleSort(arr) {
  var flag=false;  
   for(var i=0;i<arr.length-1;i++){  
    for(var j=0;j<arr.length-1-i;j++){
      if(arr[j]>arr[j+1]){
        var temp=arr[j];
        arr[j]=arr[j+1];
        arr[j+1]=temp;
        flag=true;
      }
    }
    if(flag){
      flag=false;
    }else{
     break;
    }
  }
/* 输出结果 */
                for (var k = 0; k < arr.length; k++) {
                    document.write(arr[k] + ",");
                }
                document.write("<br />");
                /* 输出结果结束 */
            
        }

 

选择排序:

每一趟从待排序的数据元素中选出最小(或最大)的一个元素,然后和最小或者最大的位置的元素交换位置,直到所有元素排完。 选择排序是不稳定的排序方法。
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [76 97 65 49 ]
第五趟排序后 13 27 38 49 49 [97 65 76]
第六趟排序后 13 27 38 49 49 65 [97 76]
第七趟排序后 13 27 38 49 49 65 76 [97]
最后排序结果 13 27 38 49 49 65 76 97
选择排序的交换操作介于 0 和 (n - 1) 次之间。选择排序的比较操作为 n (n - 1) / 2 次之间。选择排序的赋值操作介于 0 和 3 (n - 1) 次之间。
比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。交换次数O(n),最好情况是,已经有序,交换0次;
最坏情况是,逆序,交换n-1次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。

 function selectSort(array) {
            var min, temp; ;
            for (var i = 0; i < array.length; i++) {
                min = i;
                for (var j = i + 1; j < array.length; j++) {
                    if (array[min] > array[j])
                        min = j;
                }
                if (min != i) {
                    temp = array[i];
                    array[i] = array[min];
                    array[min] = temp;
                }
                /* 输出结果 */
                document.write("第 + i + "遍排序的结果是:")
                for (var n = 0; n < array.length; n++) {
                    document.write(array[n] + ",");
                }

                document.write("<br />")
                /* 输出结果结束 */

            }
        }

 

直接插入排序:

每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。
直接插入排序属于稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。
function insertSort(array) {
            var temp;
            for (var i = 1; i < array.length; i++) {
                var temp = array[i];
                for (var j = i; j > 0; j--) {
                    if(temp < array[j - 1]){
                        array[j] = array[j - 1];
                    }
                }
                array[j] = temp;
                document.write("第?" + i + "遍排序的结果是:")
                for (var n = 0; n < array.length; n++) {
                    document.write(array[n] + ",");
                }

                document.write("<br />");
            }
        }

 

快速排序:

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

值得注意的是,快速排序是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

        var count = 0;
        function quickSort(array, low, high) {
            var temp;           
            if (low < high) {
                var keypoint = QuickSortHelp(array, low, high);
                count++;
                document.write("<br />第" + count + "遍排序的结果是:")
                for (var l = 0; l < array.length; l++) {
                    document.write(array[l] + ",");
                }
                quickSort(array, low, keypoint - 1);
                quickSort(array, keypoint + 1, high);               
                }
        }
        function QuickSortHelp(array, low, high) {
            while (low < high) {
                while (low < high && array[low] <= array[high]) {
                    high--;
                }
                temp = array[low];
                array[low] = array[high];
                array[high] = temp;
                while (low < high && array[low] <= array[high]) {
                    low++
                }
                temp = array[low];
                array[low] = array[high];
                array[high] = temp;
            }
            return low;
        }    

 

堆排序:

堆的定义:n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):

(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n),当然,这是小根堆,大根堆则换成>=号。//k(i)相当于二叉树的非叶子节点,K(2i)则是左子节点,k(2i+1)是右子节点。
大根堆排序算法的基本操作:
① 初始化操作:将R[1..n]构造为初始堆;
② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。
function heapSort(array) {
            var temp;
            var i;
            for (i = Math.floor(array.length / 2); i >= 0; i--) {
                heapAdjust(array, i, array.length - 1); //将数组array构建成一个大顶堆
            }
            for (i = array.length - 1; i >= 0; i--) {
                /*把根节点交换出去*/
                temp = array[i];
                array[i] = array[0];
                array[0] = temp;
                /*余下的数组继续构建成大顶堆*/
                heapAdjust(array, 0, i - 1);
                /* 输出结果 */
                document.write("<br />第 + (array.length - i).toString() + "遍排序的结果是:")
                for (var n = 0; n < array.length; n++) {
                    document.write(array[n] + ",");
                }
                /* 输出结果结束 */
            }
        }
        //要调整的子树
        //start为数组开始下标
        //max是数组结束下标
        function heapAdjust(array, start, max) {
            var temp, j;
            temp = array[start];//temp是根节点的值
            for (j = 2 * start; j < max; j *= 2) {
                if (j < max && array[j] < array[j + 1]) {  //取得较大孩子的下标
                    ++j;
                }
                if (temp >= array[j])
                    break;
                array[start] = array[j];
                start = j;
            }
            array[start] = temp;
        }

 

希尔排序:

希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

 

 function shallSort(array) {
           var increment = array.length;
           var i
           var temp; //暂存
           var count = 0;
           do {
               increment = Math.floor(increment / 3) + 1;
               for (i = increment; i < array.length; i++) {
                   if (array[i] < array[i - increment]) {
                       temp = array[i];
                       for (var j = i - increment; j > 0 && temp < array[j]; j -= increment) {

                           array[j + increment] = array[j];

                       }
                       array[j + increment] = temp;
                       /* 输出结果 */
                       count++;
                       document.write("<br />第 + count + "遍排序的结果是:")
                       for (var n = 0; n < array.length; n++) {
                           document.write(array[n] + ",");
                       }
                       /* 输出结果结束 */
                   }
               }
           }
           while (increment > 1)

       }

 

归并排序:

 

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
值得注意的是归并排序是一种稳定的排序方法。

 

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序,称为2-路归并

 

归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数(逆序数)为:3+4+4=11;
        //source源数组

        //dest目标数组
        //s起始下标
        //t目标下标
        function mSort(source, dest, s, t) {
            var m; //取中间值
            var dest2 = new Array();
            if (s == t) {
                dest[s] = source[s];
             
            }
            else {
                m = Math.floor((s + t) / 2);
                mSort(source, dest2, s, m);
                mSort(source, dest2, m+1 , t);
                merge(dest2, dest, s, m, t);
                /* 输出结果 */
                document.write("<br />第 + ++count + "遍排序的结果是:")
                for (var n = 0; n < dest.length; n++) {
                    document.write(array[n] + ",");
                }
                /* 输出结果结束 */
            }

        }
        
        //将两个数组按照从小到大的顺序融合
        //source原数组
        //dest排序后的数组
        //s第一个下标
        //m第二个数组下标
        //总长度
        function merge(source, dest, s, m, n) {
            for (var j = m+1, k = s; j <= n && s <= m; k++) {
                if (source[s] < source[j]) {
                    dest[k] = source[s++];
                }
                else {
                    dest[k] = source[j++];
                }
            }
           
                //将剩余排不完的有序数组加入到dest的末端
                if (s <= m) {
                    for (var l = 0; l <= m - s; l++) {
                        dest[k + l] = source[s+l];
                    }
                }
                if (j <= n) {
                    for (var l = 0; l <= n - j; l++) {
                        dest[k + l] = source[j+l];
                    }
                
            }
        }

 

posted @ 2013-10-02 21:50  卡可美  阅读(330)  评论(0)    收藏  举报