文章插图
一、排序
- 冒泡排序
- 插入排序
- 当目标是升序排序,最好情况是序列本来已经是升序排序,那么只需比较n-1次,时间复杂度O(n) 。最坏情况是序列本来是降序排序,那么需比较n(n-1)/2次,时间复杂度O(n^2) 。所以平均来说,插入排序的时间复杂度是O(n^2) 。显然,次方级别的时间复杂度代表着插入排序不适合数据特别多的情况,一般来说插入排序适合小数据量的排序 。
- 快速排序
在这段代码中,我们可以看到,这段代码实现了通过pivot区分左右部分,然后递归的在左右部分继续取pivot排序,实现了快速排序的文本描述,也就是说该的算法实现本质是没有问题的 。
虽然这种实现方式非常的易于理解 。不过该实现也是有可以改进的空间,在这种实现中,我们发现在函数内定义了left/right两个数组存放临时数据 。随着递归的次数增多,会定义并存放越来越多的临时数据,需要Ω(n)的额外储存空间 。
因此,像很多算法介绍中,都使用了原地(in-place)分区的版本去实现快速排序,我们先介绍什么是原地分区算法 。
原地(in-place)分区算法描述
- 从数列中挑出一个元素,称为"基准"(pivot),数组第一个元素的位置作为索引 。
- 遍历数组,当数组数字小于或者等于基准值,则将索引位置上的数与该数字进行交换,同时索引+1
- 将基准值与当前索引位置进行交换
原地分区算法实现
// 交换数组元素位置function swap(array, i, j) { var temp = array[i]; array[i] = array[j]; array[j] = temp;}function partition(array, left, right) { var index = left; var pivot = array[right]; // 取最后一个数字当做基准值,这样方便遍历 for (var i = left; i < right; i++) { if (array[i] <= pivot) { swap(array, index, i); index++; } } swap(array, right, index); return index;}因为我们需要递归的多次原地分区,同时,又不想额外的地址空间所以,在实现分区算法的时候会有3个参数,分别是原数组array,需要遍历的数组起点left以及需要遍历的数组终点right
最后返回一个已经排好序的index值用于下次递归,该索引对应的值一定比索引左侧的数组元素小,比所有右侧的数组元素大 。
再次基础上我们还是可以进一步的优化分区算法,我们发现 <=pivot可以改为<pivot,这样可以减少一次交换
原地分区版快速排序实现
function quickSort(array) { function swap(array, i, j) { var temp = array[i]; array[i] = array[j]; array[j] = temp; } function partition(array, left, right) { var index = left; var pivot = array[right]; // 取最后一个数字当做基准值,这样方便遍历 for (var i = left; i < right; i++) { if (array[i] < pivot) { swap(array, index, i); index++; } } swap(array, right, index); return index; } function sort(array, left, right) { if (left > right) { return; } var storeIndex = partition(array, left, right); sort(array, left, storeIndex - 1); sort(array, storeIndex + 1, right); } sort(array, 0, array.length - 1); return array;}
推荐阅读
- iOS常用调试方法:断点调试
- iOS常用调试方法:静态分析
- iOS常用调试方法:LLDB命令
- 女性新手必看:零基础健身房训练干货帖
- 手机信号栏常用“符号”大揭秘!
- JavaScript 引用类型
- WordPress常用的函数、方法汇总
- 5种最常用的黑客工具,以及如何防御
- python中全部关于字符串常用操作的总结
- JavaScript函数的6个基本术语