快速排序算法入门

 对计算机存储的数据执行的最常见的操作就是排序和检索。
接下来我们要讲解的算法都是依赖数组来存储数据的,也就是js形式的排序算法方式。
讲到排序,我们首先就要有一个可测试的数据, 也就是创建一个数据测试平台里的第一步,生成**乱序的一个数组**。




    function CArray() {
        this.dataStore = [];
        this.pos = 0;
        this.setData = setData;
        this.swap = swap;
        this.length = parseInt(arguments[0]);
    }
    function setData() {
        for(var i = 0; i < this.length; i++) {
            var item = Math.floor(Math.random()*(this.length + 1));
            this.dataStore.push(item);
        }
    }
这样,我们首先就有了乱序的数据了
比如,


        var a = new CArray(10);
        a.setData();
        console.log(a.dataStore.toString());
![image.png](https://upload-images.jianshu.io/upload_images/2986075-bb0be71005d3551a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
噢,这里我直接用参数的第一个作为数组长度。
![image.png](https://upload-images.jianshu.io/upload_images/2986075-379a5f920623611e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


比较算法,数据量一般都是挺大的,因此我们可以用每10个数据一行的方式排列下去。


        CArray.prototype.toString = function() {
            var str = '';
            for(var i = 0; i < this.length; i++) {
                if(i % 10 == 9) {
                    str += this.dataStore[i] + ' ' + '\n';
                } else {
                    str += this.dataStore[i]+ ' ';
                } 
            }
            return str;
        }
        
        var a = new CArray(20);
        a.setData();
        console.log(a.toString());
![image.png](https://upload-images.jianshu.io/upload_images/2986075-1f0717e7efdafa39.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


好的,数据已经出来了,开始排序
##基本排序算法


这里我们首先增加一个**交换数值位置**的函数。


    CArray.prototype.swap= function(arr, index1, index2) {
        var item = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = item;
    }
####冒泡排序
最慢的排序方式之一,复杂度为O(n²)。
此算法中,算法会在数组中多次移动。**比较左右相邻的数据,当左侧大于右侧时进行交换。**


算法实现
        
    CArray.prototype.bubbleSort = function() {
      for(var j = 1; j < this.length; j++)
      for(var i = 0; i < this.length -j; i++) {
          if(this.dataStore[i] > this.dataStore[i+1]) this.swap(this.dataStore,i, i+1);
      }
    }


        var a = new CArray(20);
        a.setData();
        console.log(a.toString());
        a.bubbleSort();
        console.log(a.toString());


![image.png](https://upload-images.jianshu.io/upload_images/2986075-61e9ee6f891f54b7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


####选择排序
- 第一个元素和其他元素进行比较,比较完之后,最小的元素会被放到数组的第一个位置
- 然后算法从第二个位置继续。


        CArray.prototype.selectionSort = function () {
            for (var j = 0; j < this.length - 1; j++) {
                var min = j;
                for (var i = j + 1; i < this.length; i++) {
                    if (this.dataStore[min] > this.dataStore[i]) {
                        min = i;
                    }
                }
                this.swap(this.dataStore, j, min);
            }
        }


        var a = new CArray(20);
        a.setData();
        console.log(a.toString());
        a.selectionSort();
        console.log(a.toString());
![image.png](https://upload-images.jianshu.io/upload_images/2986075-0793af36bd041282.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


####插入排序
插入排序有两个循环。
- 外循环将数组元素挨个移动,内循环对外循环中选中的元素及他后面的那个元素怒进行比较。
- 如果外循环中选中的元素比内循环中选中的元素小,那么数组元素会向右移动,为内循环中的这个元素腾出位置。


        // 跑到前面,只跟在他前面的数进行对比
        // 比如第一个就是只跟第0个比较,第n就是跟前面n-1个数比较,不跟在他后面的数比较


        CArray.prototype.insertionSort = function() {
            var temp, inner;
            for (var outer = 1; outer < this.length; outer++) {
                temp = this.dataStore[outer];
                inner = outer;
                while(inner > 0 && (this.dataStore[inner - 1] >= temp)) {
                    this.dataStore[inner] = this.dataStore[inner - 1];
                    --inner;
                }
                this.dataStore[inner] = temp;
            }
        }
        var a = new CArray(30);
        a.setData();
        console.log(a.toString());
        a.insertionSort();
        console.log(a.toString());
![image.png](https://upload-images.jianshu.io/upload_images/2986075-ac7c5fd5d0325252.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


> 数据小的时候,其实这些算法所用的时间都是差不多的,当数据很大的时候才会出现差别。这三种算法中插入排序是最快的,冒泡排序是最慢
##高级排序算法
#####希尔排序
 希尔排序的工作原理是通过定义**一个间隔序列**来表示在排序过程中进行比较的元素之间有多远的间隔。
与插入排序不同,他会**首先比较距离较远**的元素。


先在CArray构造函数中添加`this.gaps=[3, 1]`,像这样
![image.png](https://upload-images.jianshu.io/upload_images/2986075-df4c2a782b9d2bb5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


        CArray.prototype.shellSort = function () {
            for (var g = 0; g < this.gaps.length; g++) {
                for (var i = this.gaps[g]; i < this.dataStore.length; ++i) {
            // 假设间距为3, 则第三个与第0个做比较,
            // 比较第6个与第3个,若第6个小于第3个则交换位置,再与第0个做比较
            // 若第9个小于第6个,则9, 6交换位置,之后重复进行第二步操作
            while(i >= this.gaps[g] && this.dataStore[i-this.gaps[g]] > this.dataStore[i]) {
                this.swap(this.dataStore, i, i - this.gaps[g]);
                i -= this.gaps[g];
                }
              }
            }
          }
        var a = new CArray(20);
        a.setData();
        console.log(a.toString());
        a.shellSort();
        console.log(a.toString());
![image.png](https://upload-images.jianshu.io/upload_images/2986075-0ae02255360faf43.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


然而,其实这个排序更重要的是如何确定初始间隔值,如何动态的确定间隔序列呢?
在《算法》 中有这样一个公式


        var N = this.dataStore.length;
        var h = 1;
        while(h < N/3) {
            h = 3 * h + 1;
        }
虽然不明白为什么是用3作为每次gaps的间隔,但还是将就一点吧
因此,我们可以重新定义shellSort函数


    CArray.prototype.shellSort1 = function() {
        var N = this.dataStore.length;
        var h = 1;
        while(h < N/3) {
            h = 3 * h + 1;
        }
        while(h >= 1) {
            for(var i = h; i < this.dataStore.length; i++) {
                while(i >= h && this.dataStore[i-h] > this.dataStore[i]) {
                    this.swap(this.dataStore, i, i - h);
                    i -= h;
                }
            }
            h = (h - 1) / 3;
        }
    }
![image.png](https://upload-images.jianshu.io/upload_images/2986075-2405807868eaf060.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


####归并排序
 归并排序的实现原理是把一系列**排好序的子序列**合并成一个大的完整有序序列。
由于自顶向下的归并排序会使用递归的算法实现, 这个算法对与庞大的数据量来说,递归的深度太深了,所以我们经常使用的是**自底向上的归并排序方式**。


- 先将数据集分解成一组只有一个元素的数组
- 然后创建一个左右子数组将他们慢慢合并起来,每次都保存排好序的数据。
![image.png](https://upload-images.jianshu.io/upload_images/2986075-0195fba8e8721468.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


这是通过每个小组分别排序进行的。emmmm,内部又是怎么排序的呢?小组内部就是比较大小咯。
![image.png](https://upload-images.jianshu.io/upload_images/2986075-b3ca9e6938889d1f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


开始算法


        CArray.prototype.mergeSort = function () {
    var step = 1;
    // 每次两组两组的合并
    while (step < this.length) {
        var left = 0;
        var right = step;
        while (right + step < this.length) {
            this.mergeArrays(this.dataStore, left, left + step, right, right + step);
            left = right + step;
            right = left + step;
        }
        if (right < this.length) {
            this.mergeArrays(this.dataStore, left, left + step, right, this.length);
        }
        step *= 2;
    }
    }
    CArray.prototype.mergeArrays = function (arr, leftStart, leftStop, rightStart, rightStop) {
        var leftArr = [];
        var rightArr = [];
        for(var k = leftStart; k < leftStop; k++) {
            leftArr.push(arr[k]);
        }


        for(var j = rightStart; j < rightStop; j++) {
            rightArr.push(arr[j]);
        }
    // 进入两个无穷大,即在永远不会超过数组 leftArr 和 rightArr的长度
        leftArr.push(Infinity);
        rightArr.push(Infinity);
        var m = 0;
        var n = 0;
        for (var k = leftStart; k < rightStop; k++) {
            if (leftArr[m] > rightArr[n]) {
                arr[k] = rightArr[n];
                n++;
            } else {
                arr[k] = leftArr[m];
                m++;
            }
        }
    }
    var a = new CArray(10);
    a.setData();
    console.log(a.toString());
    a.mergeSort();
    console.log(a.toString());
![image.png](https://upload-images.jianshu.io/upload_images/2986075-bf5b4b521742b89c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


这个归并排序讲起来不难,但实现起来经常会有一些小bug,也是很需要小心的了。


####快速排序
快速排序是处理大数据集最快的排序算法之一。通过递归分解为较小元素和较大元素的不同子序列,不断重复该步骤直到所有数据有序。
这里运用到的是递归,所以我们就不写在构造器里面了,我们直接写成一个函数的形式。


        function quickSort() {
          var arr = arguments[0];
          if(arr.length == 0) {
              return [];
          }
          var middle = arr[0];
          var greater = [];
          var lesser = [];
          for(var i = 1; i < arr.length; i++) {
              if(arr[i] < middle) {
                  lesser.push(arr[i]);
              } else {
                  greater.push(arr[i]);
              }
          }
          return quickSort(lesser).concat(middle, quickSort(greater));
      }


之后再使用call方法调用这个函数


      var a = new CArray(10);
      a.setData();
      console.log(a.toString());
      a.dataStore = quickSort.call(a, a.dataStore);
      console.log(a.toString());
![image.png](https://upload-images.jianshu.io/upload_images/2986075-e99fee8e6a188761.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
总结,快速排序算法是**比较适合于大型数据**。在小数据集的时候性能反而会下降,因为这个递归真是太多了吧,,如果小数据集的话可能跑过程都很久了。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值