1、说说你知道的排序算法?及它们的时间空间复杂度?
2、手写冒泡排序?传入第二个参数控制升序与降序?
两数交换
前菜:排序必然会涉及到两数交换。
常规操作:用临时变量temp
保存值
function swap(arr, indexA, indexB) {
let temp;
temp = indexA
indexA = indexB
indexB = temp
}
基于ES6解构赋值
function swap(arr, indexA, indexB) {
[arr[indexA], arr[indexB]] = [arr[indexB], arr[indexA]];
}
冒泡排序 Bubble Sort
冒泡排序是一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
算法描述
- 从头开始,比较前后两个元素的大小,反序则交换,直到将最大元素置于末位;
- 末位已经确定,继续比较剩下n-1个元素,重复步骤1的操作,第二大元素确定;
- 重复以上操作。
动图演示
代码实现
function bubbleSort(arr) {
for (let i = arr.length - 1; i > 0; i--) {
for (let j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
return arr;
}
传入第二个参数控制升序与降序?
function bubbleSort(arr, compareFunc) {
for (let i = arr.length - 1; i > 0; i--) {
for (let j = 0; j < i; j++) {
if (compareFunc(arr[j], arr[j + 1]) > 0) {
swap(arr, j, j + 1);
}
}
}
return arr;
}
算法属性
- 稳定
- 时间复杂度 O(n²)
- 空间复杂度 O(1)
选择排序(Selection Sort)
选择排序(Selection-sort) 是一种简单直观的排序算法。大致的思路是找到数据结构中的最小值并将其放置在第一位,接着找到第二小的值并将其放在第二位,以此类推
代码实现
❌以下是我写代码,一看说这样并没有符合选择排序的原理,选择排序并不是如一下代码实现那样,遇到两两反序,就立即执行交换操作:
function selectSort(arr){
for(let i = arr.length-1; i > 0; i--){
for(let j = 0; j<=i;j++){
if(arr[j]>arr[i]){
[arr[j], arr[i]]= [arr[i], arr[j]]
}
}
}
return arr
}
✅代码:应该使用一个最小索引变量记住每一趟遍历比较中的最小值所处索引
function selectSort(arr){
let len = arr.length,indexMin;
for(let i = 0; i < len - 1; i++){
indexMin = i
for(let j = i; j < len;j++){
if(arr[indexMin] > arr[j]){
indexMin = j
}
}
[arr[i], arr[indexMin]]= [arr[indexMin],arr[i]]
}
return arr
}
算法属性
- 不稳定
- 时间复杂度 O(n²)
- 空间复杂度 O(1)
选择排序为啥是不稳定排序?
比如:A 90、B 90、C 80
第一趟排序会将C与A替换,得到CBA
稳定的排序算法应该是CAB。
插入排序(Insertion Sort)
插入排序(Insertion-Sort) 的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
算法描述
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5。
动图演示
代码实现
function insertionSort(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;
}
return arr;
}
算法属性
- 稳定
- 时间复杂度 O(n²)
- 空间复杂度 O(1)
归并排序(Merge Sort)
JavaScript的Array类定义了一个sort函数(Array.prototype.sort)用 以排序JavaScript数组(我们不必自己实现这个算法)。ECMAScript没有定义用哪 个排序算法,所以浏览器厂商可以自行去实现算法。例如,Mozilla Firefox使用 归并排序 作为Array.prototype.sort的实现,而Chrome使用了一个 快速排序 (下面我们会学习的)的变体。——《JavaScript算法与数据结构》
归并排序是一种分治算法。其思想是将原始数组切分成较小的数组,直到每个小数组只有一 个位置,接着将小数组归并成较大的数组,直到最后只有一个排序完毕的大数组。
算法描述
- 分解(Divide):将n个元素分成个含n/2个元素的子序列。
- 解决(Conquer):用合并排序法对两个子序列递归的排序。
- 合并(Combine):合并两个已排序的子序列已得到排序结果。
动图演示
代码实现
function mergeSort(arr){
let len = arr.length
if(len === 1) return arr;
let min = Math.floor(len / 2),
left = arr.slice(0,min),
right = arr.slice(min,len);
return merge(mergeSort(left),mergeSort(right))
}
function merge(left,right) {
let result = [];
while(left.length>0 && right.length>0){
if(left[0]>=right[0]){
result.push(right.shift());
}else{
result.push(left.shift());
}
}
while(left.length){
result.push(left.shift());
}
while(right.length){
result.push(right.shift());
}
return result;
}
算法属性
- 稳定
- 时间复杂度 O(nlogn)
- 空间复杂度 O(1)
快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述
- 首先,从数组中选择中间一项作为主元。
- 创建两个指针,左边一个指向数组第一个项,右边一个指向数组最后一个项。移动左指 针直到我们找到一个比主元大的元素,接着,移动右指针直到找到一个比主元小的元素,然后交 换它们,重复这个过程,直到左指针超过了右指针。这个过程将使得比主元小的值都排在主元之 前,而比主元大的值都排在主元之后。这一步叫作划分操作。
- 接着,算法对划分后的小数组(较主元小的值组成的子数组,以及较主元大的值组成的 子数组)重复之前的两个步骤,直至数组已完全排序。
算法视频
B站找了一个通过跳舞视频演示快排的: 舞动的排序算法 快速排序
这个舞蹈算法也是经典了,整个系列都值得推荐观看,相比很多动图演示更生动。
代码实现
function quickSort(arr, left, right) {
let len = arr.length,
partitionIndex,
left = typeof left != 'number' ? 0 : left,
right = typeof right != 'number' ? len - 1 : right;
if (left < right) {
partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex-1);
quickSort(arr, partitionIndex+1, right);
}
return arr;
}
function partition(arr, left ,right) { // 分区操作
let pivot = left, // 设定基准值(pivot)
index = pivot + 1;
for (var i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index-1;
}