十种经典排序算法 JavaScript实现

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。

在这里插入图片描述
****此图中希尔排序有误,希尔排序最好情况为O(nlogn)最坏情况为O(n²)

冒泡排序(Bubble Sort)

算法描述

  • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;【此时生成序列为降序】
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  • 针对所有的元素重复以上的步骤,除了最后一个;
  • 重复步骤1~3,直到排序完成。
    (优化部分:若在某一组比较过程中没有进行交换,则代表剩余的数已经遵循顺序排列,此时可以直接退出排列)

在这里插入图片描述

function swap(arr, i, j) { //交换两个下标的内容
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
function bubbleSort(arr) { //冒泡排序
    var flag //标记是否进行了交换
    for (let i = 0; i < arr.length - 1; i++) {
        flag = 0
        for (let j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1)
                flag = 1 //进行交换 标记为1
            }
        }
        if (flag == 0)
            break //若本轮中没有进行交换,则直接退出循环
    }
    return arr
}

选择排序(Select Sort)

算法描述

  • 初始状态:无序区为R[1…n],有序区为空;
  • 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录
  • R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
  • n-1趟结束,数组有序化了。

例如
对于数列中[2,4,1,5,3,8],一开始的有序区长度为0,在整个数组中找出最小的数字为1,把1 和 2 的位置交换,得到 [1,4,2,5,3,8], 第一趟排序完成后,有序区长度+1,此时在无序区[4,2,5,3,8]中找出最小的为2,把2和4交换…直到所有数字比较完成
在这里插入图片描述
***是表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。

function selectSort(arr) {
   let len = arr.length
    for (let i = 0; i < len - 1; i++) {
        let minIndex = i
        for (let j = i + 1; j < len; j++) {
            if (arr[minIndex] > arr[j])
                minIndex = j 
        }
        swap(arr, minIndex, i) //交换位置
    }
    return arr
}

插入排序 (Insert Sort)

算法描述

在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
在这里插入图片描述

function insertSort(arr) {
    let len = arr.length
    for (let i = 1; i < len; i++) {
        let preIndex = i - 1
        let current = arr[i]
        while (preIndex >= 0 && arr[preIndex] > current) { //此处需要注意 不可以用arr[i]代替current,因为通过后面的置换arr[i]的值会发生改变
            arr[preIndex + 1] = arr[preIndex]
            preIndex--
        }
        arr[preIndex + 1] = current
    }
    return arr
}

希尔排序(Shell Sort)

算法描述

  • 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  • 按增量序列个数k,对序列进行k 趟排序;
  • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1
    时,整个序列作为一个表来处理,表长度即为整个序列的长度

(按照增量分成几个小数组来排序,增量的算法为

var gap = 1
while (gap < len / 3) {
    gap = gap * 3 + 1
}
//也有直接用gap = Math.floor(len/2)的算法

在这里插入图片描述

function shellSort(arr) {
    var len = arr.length
    var gap = 1
    while (gap < len / 3) {
        gap = gap * 3 + 1
    }
    for (gap; gap > 0; gap = Math.floor(gap / 3)) {
        var temp;
        for (let i = gap; i < len; i++) {
            temp = arr[i]
            for (var j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {
                arr[j + gap] = arr[j]
            }//此处运用到插入排序算法
            arr[j + gap] = temp
        }
    }
    return arr
}

归并排序(Merge Sort)

算法描述

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。

在这里插入图片描述

function mergeSort(arr) {
    var len = arr.length
    if (len < 2) {
        return arr
    }
    var middleIndex = Math.floor(len / 2)
    var left = arr.slice(0, middleIndex)
    var right = arr.slice(middleIndex)
    return merge(mergeSort(left), mergeSort(right))
}
function merge(left, right) {
    var result = []
    while (left.length && right.length) {
        if (left[0] < right[0])
            result.push(left.shift())
        else
            result.push(right.shift())
    }
    while (left.length) {
        result.push(left.shift())
    }
    while (right.length) {
        result.push(right.shift())
    }
    return result
}
//push是在数组的最后插入内容
//shift为删除数组的第一个值并且返回

快速排序(Quick Sort)

算法描述

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

在这里插入图片描述

// 6.快速排序 quickSort()
function quickSort(arr, left, right) {
    var len = arr.length
    left = typeof left != 'number' ? 0 : left //如果left值不存在则设置为0
    right = typeof right != 'number' ? len - 1 : right
    if (left < right) {
        var partitionIndex = partition(arr, left, right)
        left = quickSort(arr, left, partitionIndex - 1)
        right = quickSort(arr, partitionIndex + 1, right)
    }
    return arr
}
function partition(arr, left, right) {
    let index = left + 1
    for (let i = left + 1; i <= right; i++) {
        if (arr[i] < arr[left]) {
            swap(arr, index, i)
            index++
        }
    }
    swap(arr, index - 1, left)
    return index - 1
}
quickSort(arr)

堆排序(Heap Sort)

算法描述

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:

大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

  • 创建一个堆 H[0……n-1];
  • 把堆首(最大值)和堆尾互换;
  • 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
  • 重复步骤 2,直到堆的尺寸为 1。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
// 7.堆排序 
function heapSort(arr){
    buildMaxHeap(arr)
    var flag = arr.length
    for(i=arr.length-1;i>=0;i--){
        swap(arr,0,i)
        heapify(arr,--flag,0)
    }
    return arr
}
function heapify(arr,len,i){
    var maxIndex = i
    var left = 2*i+1
    var right = 2*i+2
    if(left<len&&arr[left]>arr[maxIndex]){
        maxIndex = left
    }
    if(right<len&&arr[right]>arr[maxIndex]){
        maxIndex = right
    }
    if(maxIndex!=i){
        swap(arr,i,maxIndex)
        heapify(arr,len,maxIndex)
    }
}
function buildMaxHeap(arr){
    var len = arr.length
    for(let i=Math.floor(len/2);i>=0;i--){
        heapify(arr,len,i)
    }
}

计数排序(Count Sort)

算法描述

  • 找出待排序的数组中最大和最小的元素
  • 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  • 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  • 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

在这里插入图片描述

// 8.计数排序
function countingSort(arr) {
    var count = []
    for (let i = 0; i < arr.length; i++) {
        if (!count[arr[i]])
            count[arr[i]] = 0
        count[arr[i]]++
    }
    var len = arr.length
    var i = 0
    var flag = 0
    while (len) {
        while (count[i]) {
            arr[flag++] = i
            count[i]--
            len--
        }
        i++
    }
    return arr
}

桶排序(Bucket Sort)

算法描述

先把数字放在规定的桶内
在这里插入图片描述
在桶内进行排序,排序完成后再依次把每个桶里面依次取出来
在这里插入图片描述

// 9.桶排序
function bucketSort(arr, bucketSize) {
    if (arr.length === 0) {
        return arr;
    }
    var len = arr.length
    var maxValue = arr[0]
    var minValue = arr[0] 
    //取出arr中的最大值和最小值
    for (let i = 1; i < len; i++) {
        if (arr[i] > maxValue) {
            maxValue = arr[i]
        }
        else if (arr[i] < minValue) {
            minValue = arr[i]
        }
    }
    //如果没有设置桶的数量,则默认为5
    bucketSize = typeof bucketSize != 'number' ? 5 : bucketSize
    //每个桶的大小设置,(最大值-最小值)/桶的数量+1
    var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1
    var buckets = new Array(bucketCount)
    for (let i = 0; i < buckets.length; i++) {
        buckets[i] = [] //二维数组的设置
    }
    for (let i = 0; i < len; i++) {
        buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i])
    }
    arr.length = 0 //把arr置空
    for (let i = 0; i < buckets.length; i++) {
        insertSort(buckets[i])
        for (let j = 0; j < buckets[i].length; j++) {
            arr.push(buckets[i][j]) //把桶内的值依次放回arr中
        }
    }
    return arr
}

基数排序(Radix Sort)

算法描述

  • 根据数组内的最大值来判断要根据位数来依次摆放
  • 先根据个位数 0结尾的放在buckets[0]的数组中 1结尾的放在buckets[1]的数组中 …
  • 把刚刚存好的buckets从[0]依次取出放到arr中
  • 根据十位数 0结尾的放在buckets[0]的数组中 1结尾的放在buckets[1]的数组中 …
  • 不断重复直到最大值的位数都执行完毕
    在这里插入图片描述
//在菜鸟教程看过这一段,循环次数太多了,算是稍微改进了一下
function radixSort(arr, maxDigit) {
    var mod = 10
    var dev = 1
    var buckets = new Array(10)
    for (let i = 0; i < 10; i++) {
        buckets[i] = []
    }
    for (mod = 10; mod < maxDigit * 10; mod *= 10, dev *= 10) {
        for (let i = 0; i < arr.length; i++) {
            buckets[parseInt(arr[i] % mod / dev)].push(arr[i])
        }
        arr.length = 0
        for (let j = 0; j < 10; j++) {
            while (buckets[j].length) {
                arr.push(buckets[j].shift())
            }
        }
    }
    return arr
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值