这次主要讲几个经典的排序:
- 冒泡排序
- 插入排序
- 选择排序
- 归并排序
- 快速排序
- 计数排序
- 桶排序
按时间复杂度度分类
- O(n^2): 冒泡、插入、选择
- O(nlogn): 快排、归并
- O(n): 桶、计数、基数
一般情况下我们会从以下几个方面来衡量:
- 最好情况、最坏情况、平均情况时间复杂度
- 时间复杂度的系数、常数、低阶
- 比较次数和交换次数
排序算法的内存消耗
内存消耗是通过空间复杂度来衡量
排序算法的稳定性
就是说序列中存在值相等的元素,经过排序之后,相等元素之前原有的先后顺序不变。
冒泡排序
function bubbleSort(arr:Array) { const array = [...arr];2 const length = array.length; if(!array.length){ return; } for(let i = 0; i { let t = -1; if(array[index] > array[index+1]){ t = array[index]; array[index] = array[index+1]; array[index+1] = t; } }); } return array; }
是原地排序法
是稳定排序法
时间复杂度O(n^2)
插入排序
说一下这个核心思想吧,就是把数据分成两个部分,一个是已排序,一个是未排序。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想就是取未排序的区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。真到未排序区间中元素为空,算法结束。
function insertSort(arr:Array) { const array = [...arr]; const length = array.length; if(!array.length){ return; } for(let i = 1; i= 0; j--){ if(array[j] > t){ array[j+1] = array[j]; }else{ break; } } array[j+1] = t; } return array; }
是原地排序法
是稳定排序法
时间复杂度O(n^2)
选择排序
选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
这个代码跟插入排序类似,不写了 但他不是稳定算法
归并排序
归并排序就是把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排序好的两部分合并在一起,这样整个数组就都有序了。
function mergeSortItem(left: Array, right: Array) { let [l, r] = [0, 0]; let result = []; while (l < left.length && r < right.length) { if (left[l] < right[r]) { result.push(left[l]); l++; } else { result.push(right[r]); r++; } } result = result.concat(left.slice(l, left.length)); result = result.concat(right.slice(r, right.length)); return result; } function mergeSort(arr: Array):Array { const array = [...arr]; const length = array.length; if (length === 1) { return array; } const num = Math.floor(length / 2); const arrayLeft = array.slice(0, num); const arrayRight = array.slice(num, length) return mergeSortItem(mergeSort(arrayLeft), mergeSort(arrayRight)); }
快速排序
就是在一组数据中随机找个数作为中间点,小于中间点的放在左边,大于中间点的放在右边。
function quickSort(arr: Array):Array { if(arr.length < 2){ return arr; } const index = Math.floor(arr.length/2); const element = arr[index]; arr.splice(index, 1); const left:any[] = []; const right:any[] = []; arr.map((item) => { if(item < element){ left.push(item); }else{ right.push(item); } }); return [...quickSort(left), element, ...quickSort(right)]; }
桶排序、计数排序、基数排序。因为这些排序算法的时间复杂度是线性的,所以我们把这类排序算法叫作线性排序。之所以能做到线性的时间复杂度,主要原因是,这三个算法是非基于比较的排序算法,都不涉及元素之间的比较操作。
桶排序
核心思想就是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行非序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。桶排序比较适合用在外部排序中。所谓的外部排序就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。
计数排序
计数排序其实就是桶排序的一种特殊情况。当要排序的n个数据,所处的范围并不大的时候,比如最大值是K,我们就可以把数据划分成k个桶。每个桶内的数据值都是相同的,省掉了桶内排序的时间。
计数排序只能用在数据范围不大的场景中,如果数据范围K比要排序的数据n大很多,就不适合用计数排序了。而且,计数排序只能给非负整数排序,如果在排序的数据是其他类型的,要羌其在不改变相对大小的情况下,转化为非负整数。