js实现排序算法

 相关概念

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
  • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

一、冒泡排序

//冒泡排序
function bubbleSort (arr) {
    let 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]) {
                console.log('>>>>>>')
                let temp = arr[j+1]
                arr[j+1] = arr[j]
                arr[j] = temp
            }
        }
    }
    return arr
}
let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46];
console.log('arr :', bubbleSort(arr));

二、选择排序

从0----length-1的范围内选出一个最小值将它放到位置1上,再从1----length-1的范围内选出一个最小值将它放到位置2上,以此类推
function selectSort(arr){
    let len = arr.length
    let minIndex ,temp 
    for(let i = 0; i < len - 1; i++) {
        minIndex = i //默认最小值
        for (let j = i+1; j < len; j++) {
            if(arr[j] < arr[j+1]) { //寻找最小值
                minIndex = j //最小值的索引
            }
        }
        //交换默认最小值和最小值的位置
        temp = arr[i]
        arr[i] = arr[minIndex]
        arr[minIndex] = temp
    }
}

三、插入排序

位置n上的数和它前面的n-1位置上数进行比较,如果较小就将位置n-1的数放在n位置上,再和n-2位置上的数进行比较,一直到比到比它小的数出现为止,放在那个比它小的数的后面。
function insertSort (arr) {
    let len = arr.length
    let preIndex, current
    for(let i = 1; i < len; i++) {
        preIndex = i-1
        current = arr[i]
        //结束的条件是找到已排序的元素小于或者等于当前元素的位置
        while(preIndex >= 0 && arr[preIndex] > current) { //如果前面的数大于当前元素,
            arr[preIndex+1] = arr[preIndex] //就将前一个数向后移
            preIndex-- //指针向前移动
        }
        //将当前元素插入到该位置后
        arr[preIndex+1] = current
    }
}

一、阮一峰老师的快排js实现

 思路:

1、选择数组中间数作为基数,并从数组中取出此基数;

2、准备两个数组容器,遍历数组,逐个与基数比对,较小的放左边容器,较大的放右边容器;

3、递归处理两个容器的元素,并将处理后的数据与基数按大小合并成一个数组,返回。

实现:

var quickSort = function(arr) {
 
  if (arr.length <= 1) { return arr; }
 
  var pivotIndex = Math.floor(arr.length / 2);
 
  var pivot = arr.splice(pivotIndex, 1)[0];
 
  var left = [];
 
  var right = [];
 
  for (var i = 0; i < arr.length; i++){
 
    if (arr[i] < pivot) {
 
      left.push(arr[i]);
 
    } else {
 
      right.push(arr[i]);
 
    }
 
  }
 
  return quickSort(left).concat([pivot], quickSort(right));
 
};

总结:

R的思路非常清晰,选择基数为参照,划分数组,分而治之,对于新手来理解快排的核心思想“参照-划分-递归”,很容易理解 。

既实现了排序,又符合快速排序的思想,为什么还会为人所诟病呢?原来是因为:

1、R取基数用的是splice()函数取,而不是算法中常用的取下标。基数只是一个参照对象,在比对的时候,只要能从数组中取到即可,所以只需要知道它的索引即可,调用函数删除基数只会更耗时;

2、根据基数来划分时,R专门生成两个数组来存储,从而占用了更多的存储空间(增加了空间复杂度)。

严格上讲,R的代码仅仅是用快速排序的思想实现了排序,也算是快速排序,但是还有很多改进之处。

二、某乎上的解法

基本思路跟我上述理解大同小异,主要来看看这篇文章具体的实现过程。下面借用原文的图来讲解(原文的图做的很好就不单独画图了,主要讲一讲原文没解释需要注意的地方,和对该篇文章做一个补充),底部附原文链接。

1.数组[2,3,1,5,6,4],创建两指针,一个只想头一个指向尾,再确定一个基准数。

注意:为了方便后面递归是能够确定基准数,这里基准数选取,第一个数或者最后一个数)

 

2.开始第一次的递归处理,尾指针先从右往左扫,扫到第一个小于(注意是小于,而不是小于等于哦)基准数的位置停住,这时候头指针再从左往右扫,扫到第一个大于基准数的位置停住,这时候是下面的图示状态:

注意:这里如果基准数选区的第一个数,应该尾指针先往左侧扫,若基准数选取为最后一个属则,应是头指针向往右扫)

 

交换两个指针所指的数,成为了下面的状态:

3.两个数交换完毕,右指针此时指的是arr[2] = 3, 左指针指着arr[1] = 1;交换完毕后右指针继续从当前位置往左扫,扫到1的时候发现和左指针相遇了,那么这个时候就结束左右指针的扫描,左右指针同时指着arr[1] = 1,即:

此时退出循环扫描的过程,交换基准数与左右指针同时所指的数的位置,开头说了,基准数我选择的是arr[0] = 2, 指针指的是arr[1] = 1; 交换过后就变成了:

这时候就发现基准数已经出现在了它排完序后应该在的位置(排完序后是[1,2,3,4,5,6],2出现在了第2位),比这个基准数小的数组出现在了它的左边([1]出现在了2的左边),比基准数大的出现在了它的右边([3,5,6,4]出现在了2的右边)。

4.之后的过程就是对左右数组的分别递归处理。

let quickSort = (arr, begin, end) => {
//递归出口
    if (begin >= end)
        return;
    var l = begin; // 左指针
    var r = end; //右指针
    var temp = arr[begin]; //基准数,这里取数组第一个数
    //左右指针相遇的时候退出扫描循环
    while (l < r) {
        //右指针从右向左扫描,碰到第一个小于基准数的时候停住
        while (l < r && arr[r] >= temp)
            r--;
        //左指针从左向右扫描,碰到第一个大于基准数的时候停住
        while (l < r && arr[l] <= temp)
            l++;
        //交换左右指针所停位置的数
        [arr[l], arr[r]] = [arr[r], arr[l]];
    }
    //最后交换基准数与指针相遇位置的数
    [arr[begin], arr[l]] = [arr[l], arr[begin]];
    //递归处理左右数组
    quickSort(arr, begin, l - 1);
    quickSort(arr, l + 1, end);
};
let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46];
let end = arr.length-1
console.log('arr :', quickSort(arr, 0, end));

 推荐比较好的几篇文章

https://zhuanlan.zhihu.com/p/93129029

https://www.cnblogs.com/siegaii/p/10744645.html

https://zhuanlan.zhihu.com/p/93129029

https://www.cnblogs.com/hjx-blog/articles/9183453.html

不积跬步无以至千里
原文地址:https://www.cnblogs.com/lyt0207/p/12489950.html