## 排序算法
### 首先介绍一下内排序和外排序的区别
外排序:指排序过程中排序对象不能同时放在内存当中,排序过程不断在内外存之间移动的排序 内外存结合
内排序:排序过程中所有元素都放到内存中进行排序
外排序用读写外存的次数衡量其效率,内排序根据比较次数来衡量效率
排序算法的稳定性
假定待排序的记录序列中,存在多个具有相同关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,若r[i] == r[j],且r[i]在r[j]之前,在排序之后 r[i] 仍在 r[j] 之前,则称这种排序算法是稳定的;否则是不稳定的。
1. 快速排序
快速排序是对冒泡排序的一种改进,通过在数组中取出一个数据第一趟排序时将数据分为俩个部分,大于该数据的放一侧,小于该数据的放另一侧,该部分需要O(n)次数的操作,然后递归调用,在俩侧都实现快速排序。该过程
该排序算法属于内排序,不稳定
平均时间复杂度为 O(n log n)
最好情况为: O(n log n)
最坏情况: O(n²)
空间复杂度 O (log2 n)
简析:因为排序的次数不大于比较的次数,因此快速排序的最坏时间复杂度为O(n^2)
因为时间复杂度的分析只需关注循环执行次数最多的一段代码,这段核心代码的执行次数的n的量级就是整段要分析代码的时间复杂度。
for(var i = 0; i < arr.length; i++){
if(arr[i] < midNum){
left.push(arr[i])
} else {
right.push(arr[i])
}
}
以上是快速排序的核心代码,也是执行次数最多的一段代码,其中外层for循环的次数为所要排序数组的长度n,则T1(N) = O(n)
内层当arr[i]的值的大小满足一定条件时,才继续执行对应的代码这是一个对数阶的时间复杂度(所有对数阶的时间复杂度都可以记做O(logn)),记为T2(N) = O(logn)
所以总的时间复杂度为T(n) = T1 * T2 = O(nlogn)
//快速排序
function quickSort(arr){
//边界值判断
if(arr.length <= 1){
return arr;
}
var left = [],
right = [],
len = arr.length,
midIndex = Math.floor(arr.length / 2),
midNum = arr.splice(midIndex, 1)[0];
for(var i = 0; i < len; i++){
if(arr[i] < midNum){
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([midNum], quickSort(right));
}
2. 冒泡排序
冒泡排序是通过比较俩个相邻的元素,如果第一个元素大于第二个元素,那么进行交换(升序),降序相反
冒泡排序算法属于内排序,稳定
平均时间复杂度为 O( n²)
最坏情况为 O( n²)
最好情况为 O(n)
空间复杂度为 O(1) 是一个就地排序
关于冒泡排序算法的优化
只要内循环某一趟没有进入 arr[j] > arr[j + 1]的话,说明此时该数组已经排序完成,没有必要再消耗性能继续遍历已排序好了的数组了
//已优化好了的排序算法
//*
* arr: 待排序数组
* type: 定义排序类型,默认为true(升序), false(降序)
*/
function bubble(arr,type){
var i,
j,
temp,
len = arr.length,
flag = true; // 判断是否已排序好
flag = flag || true;
for(; i < len - 1; i++){
flag = true;
for(; j < len - i; j++){
if(type){
if(arr[j + 1] < arr[j]){
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
flag = false;
}
}
if(flag){
flag = false;
}
}
}
3. 选择排序
首先在未排序序列中找到最大(小)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最大(小)元素,然后放到已排序序列的末尾。以此类推,直到所有元素都排序完。
在i之前的元素是已经排序好了的,而在i之后是尚未排序好的。
选择排序属于内排序 不稳定
平均时间复杂度为:O(n²)
最好情况:O(n²)
最坏情况:O(n²)
可见选择排序在时间复杂度上无论什么数据进去都是O(n^2),数据规模越小越好,好处是不占用额外的内存。
3. js中自带的sort方法
JavaScript中的排序方法是基于原数组进行排序,不生成副本,也就是说这个方法是会改变原数组的,该方法有一个可选的参数 sortby ,用来规定排序顺序。而且该参数必须是函数,如果没有使用参数,那么sort方法将会根据字符串编码的方式进行排序。
当有使用该参数时,向该比较函数中传入俩个参数,a、b,用来定义排序规则,比如
`function ascent (a, b) {
return a - b;//升序
}
function descent (a, b) {
return b - a;//降序
}`
4.插入排序
算法思路:
类似于整理扑克的过程,将当前刚拿到的牌插入到已排序好的牌组中,插入排序有N-1趟排序组成,将该元素与之前排序好的数组的每一项比较,并将其放入合适的位置,最终得出有序数组。
基于该算法的思路来看,插入排序存在最好情况和最坏情况,以升序排序举例
最好情况 升序排列的数组 比较n-1次即可
最坏情况 降序排列的数组 比较n(n-1)次
时间复杂度:O(n^2) 这是一种稳定的排序方法
5.归并排序
算分思路:
将数组中n个记录看成n个长度为1的有序字表
进行俩俩归并使记录关键字有序,得到 n/2个长度为2的有序字表
重复第二步操作,直到所有记录都归并成一个长度为n的有序表为止。
function mergeSort(array){
var len = array.length;
if(len < 2){
return array;
}
var middle = Math.floor(len / 2),
left = array.slice(0, middle),
right = array.slice(middle),
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;
}
}
6.堆排序
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
复杂度:O (nlgn)