图解JavaScript排序算法(新手向)

337 阅读6分钟

博主是一名在校学生,最近学习了《JavaScript数据结构》,所以打算写篇博客来整理一下关于JavaScript排序算法的一些实现,也算是巩固一下知识。我会从最基础的冒泡排序讲起,最后到快速排序为止。如果你和我一样是一个算法小白,那么看完这篇文章应该会有一点点收获。😃

一、首先从最简单的冒泡排序开始

可能对于绝大多数人而言,接触到的第一种排序算法都是冒泡排序,上课的时候一般也都用它来作为入门排序算法,因为它是所有排序算法中最简单的。然而,从运行时间的角度来看,冒泡排序是性能最差的一个。

下面来看一下冒泡排序的原理以及代码实现:

冒泡排序比较所有相邻的两个项,如果第一个比第二个大,则交换它们。元素项向上移动至正确的顺序,就好像气泡升至表面一样,因此得名。

**图解: **

**代码实现:**外层for循环从数组的最后一项开始遍历,这样在接下来的排序过程中可以避免对末尾已经排好序的元素做重复遍历,内层for循环对数组元素做一个从前往后的遍历,如果发现前一项比后一项大,那么让两者交换位置。冒泡排序用到了两层for循环嵌套,这就是为什么说它是从运行时间角度来看,性能最差的排序算法。

function bubbleSort(array) {
	var length = array.length
    for(let i=length-1; i>=0; i--){
    	for(let j=0; j<i; j++) {
        	if(array[j] > array[j+1]) {
            	var temp = array[j]
                array[j] = array[j+1]
                array[j+1] = temp
            }
        }
    }
    return array
}

时间复杂度:

最好情况一般情况最差情况
O(n)O(n²)O(n²)

 

二、选择排序

选择排序算法是一种原址比较排序算法。大致思路是找到数组中的最小值并将其放到第一位,接着找到第二小的值并将其放到第二位,以此类推。选择排序改进了冒泡排序, 将交换的次数由O(n²)减少到O(n), 但是比较的次数依然是O(n²)

图解:

**代码实现:**首先外层for循环遍历整个数组,然后把最小值设为i,再从i+1开始遍历整个数组,如果array[min]大于array[j]的话,就让min = j,这个过程就是在寻找本轮遍历中的最小值。在找到了最小值之后,再把最小值放到下标i的位置,以此类推。

function selectionSort(array) {
	var length = array.length
    for(let i=0; i<length; i++) {
    	var min = i
        for(let j=i+1; j<length; j++) {
        	if(array[min] > array[j]){
            	min = j
            }
        }
        var temp = array[i]
        array[i] = array[min]
        array[min] = temp
    }
    return array
}

时间复杂度:

最好情况一般情况最差情况
O(n²)O(n²)O(n²)

 

三、插入排序

插入排序是简单排序中效率最好的一种。插入排序也是学习其他高级排序的基础,比如希尔排序/快速排序,所以也非常重要。插入排序思想的核心是局部有序,每次排一个数组项,以此方式构建最后的排序数组。假定第一项已经排序了。接着,让它和第二项进行比较——第二项是应该待在原位还是插到第一项之前呢?这样头两项就已经正确排序了,接着和第三项比较(它应该插入到第一、第二还是第三的位置呢),以此类推。插入排序的比较次数相比于选择排序少了一半,所以该算法的效率是要高于选择排序的。

**图解: **

**代码实现:**首先默认第一项是已经排好序的,然后从第一项开始遍历整个数组。用temp保存array[i],并把i赋给j,然后用while循环去遍历,只要array[j-1]>temp并且j>0,就一直进行循环。在每一轮循环中让array[j] = array[j-1],即让下标为j的元素往后移动一位,并且让j减1,再跳出循环以后,把temp赋给array[j]。接着对下一个数组元素进行相同的操作,以此类推。主要思想就是把要插入的元素拿出来和前面已经排好序的局部有序数组进行比较,然后把它插入到正确的位置。

function insertionSort(array) {
	var length = array.length
    for(let i=0; i<length; i++) {
    	var temp = array[i]
        var j = i
        while(array[j-1] > temp && j>0){
        	array[j] = array[j-1]
            j--
        }
        array[j] = temp
    }
    return array
}

时间复杂度:

最好情况一般情况最差情况
O(n)O(n²)O(n²)

 

四、希尔排序

希尔排序是按一定的间隔对数列进行分组,然后在每一个分组中做插入排序;随后逐次缩小间隔,在每一个分组中做插入排序,以此类推。直到间隔等于1,再最后进行一次插入排序。

图解:

**代码实现:**首先设定步长gap为数组长度的一半,因为是下标为j的元素和下标为j-gap的元素比较,所以从下标为gap的元素开始遍历整个数组。核心代码的话和插入排序是相同的,只不过插入排序是每次减1,而希尔排序是每次减去gap。

function shellSort(array) {
    var length = array.length
    var gap = Math.floor(length/2)
    while(gap >= 1) {
        for(let i=gap; i<length; i++) {
            var temp = array[i]
            var j = i
            while(array[j-gap] > temp && j>gap-1) {
                array[j] = array[j-gap]
                j -= gap
            }
            array[j] = temp
        }
        gap = Math.floor(gap/2)
    }
    return array
}

**时间复杂度: **

最好情况一般情况最差情况
O(nlog(n))O(nlog²(n))O(nlog²(n))
&nbsp

五、快速排序

快速排序是一种很常用的排序算法。它的复杂度为O(nlog(n)),且性能通常比其他复杂度为O(nlog(n))的排序算法要好。快速排序用到了分而治之的思想,将原始数组分为较小的数组。

**主要步骤如下: **
(1)在数据集之中,选择一个元素作为"基准"(pivot)。
(2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。
(3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

  **图解: **

function quickSort(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))
}

时间复杂度:

最好情况一般情况最差情况
O(nlog(n))O(nlog(n))O(n²)
 

由于最近在忙着弄秋招没有时间,这里暂时先列举五种排序算法,之后有时间的话还会再补充一些其他的排序算法,最后祝大家都早日上岸!

PS:封面图来自@一村那点事儿(侵删)