一、算法概述
算法分类:
常见的排序算法可以分为两大类:
- 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序
- 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序
相关概念:
- 稳定:如果a刚开始在b的前面,而且a=b,排序之后a仍然在b的前面。
- 不稳定:如果a原本在b的前面,而且a=b,排序之后a不在b的前面。
- 时间复杂度:对排序数据的总操作次数,反映当n变化的时候,操作次数呈现什么规律。
- 空间复杂度:是指算法在计算机中执行所需要的的存储空间的度量,他也是数据规模n的函数
一、arr.sort()
js提供了一种排序方法,sort,sort() 方法用于对数组的元素进行排序。
返回值:对数组的引用。请注意,数组在原数组上进行排序,不生成副本。
传入的参数为一个函数
使用实例:
let arr = [1,2,96,5,63,25,35,35,58,75,94,9,1,38];
console.log(arr,'beforeSort');
//升序
arr.sort((a,b)=>a-b);
console.log(arr,'afterascSort');
//降序
arr.sort((a,b)=>{
return b-a;
});
console.log(arr,'afterdescSort');
这里来说一下arr.sort的原理
sort方法可以直接调用,不传入任何参数,也可以传入一个比较函数作为参数,关于比较函数待会再解释,下面先说不传参的时候。
当不传入参数的时候,sort()
方法会调用默认的排序的方式,即先调用每个数组项的toString()
转型方法,然后按照字符串Unicode编码顺序来对字符串进行排序,例如下面的栗子:
var arr = [1, 2, 3, 15, 22, 33];
arr.sort();
alert(arr);
//1, 15, 2, 22, 3, 33
输出结果可能和想象中的不一样,但是这是按照这些数字在Unicode字符集中的顺序进行排序的。
那么怎么才能让他按照我们的想法对数组进行排序呢?没错,就是传入一个函数作为参数,来指明排序方式。(注意,参数必须是函数,不能是其他的)
这个函数可以像下面这样写:
function cmp (value1, value2) {
if (value1 < value2) {
return 1;
}
else if (value1 > value2) {
return -1;
} else {
return 0;
}
}
还是解释一下这个函数,如果value2大于value1,那么就会返回1,即true,那么就会执行交换,这样就会把较大的value2放在前面,则较小value1就放在后面。这样整体排下来就会是一个从大到小的排序。
还有一种简化的cmp函数的书写方法:
function cmp (a, b) {
return b - a;
}
这个函数和上面的函数作用是相同的,当b比较大的时候,就会返回一个大于等于1的数,即true,这样就会执行交换,总体结果也是一种降序排序,可以自己写代码试一下。
以上arr.sort()原理解析借鉴这篇文章:sort解析
二、冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
2.1、算法描述(升序)
- 比较相邻的两个元素,如果第一个比第二个大,交换他们两个的位置;
- 对每一对相邻的元素进行以上步骤的比较,从开始比较到最后一对,这样在最后的元素应该是最大的数,
- 针对所有的元素重复以上步骤,除了最后一个;
- 重复步骤1~3,直到排序完成
2.2、代码实现
代码实现如下:
function buddleSort(arr){
var len = arr.length;
// 一共需要比较n-1轮
for(let i = 0; i < len-1; i++){
// 每轮比较都会筛选出一个最大的或者最小的去
//,所以每比较一轮,就会减少一次
for(j=0;j<len-i-1;j++){
// 如果前面的数大于后边的,交换位置(升序)
if(arr[j]>arr[j+1]){
let temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
console.log(arr,'beforeSort');
buddleSort(arr);
console.log(arr,'afterascSort');
2.3、动图演示
冒泡排序的动图演示如下:
三、选择排序
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
3.1、算法描述
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
初始状态:无序区为R[1…n],有序区为空;
第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
n-1趟结束,数组有序化了。
每次都会遍历这些个数,假定第一个数为最小的,然后遍历整个数组,将第一个数的下标当做最小值下标,然后与其他数进行比较,如果发现比他小的,将这个更小的数的下标记录下来,当做最小值下标,重复以上步骤最后将最小值放到前面。
3.2、代码实现
代码实现如下:
function selectSort(arr){
let len = arr.length;
let minIndex,temp;
for(let i = 0; i<len-1;i++){
minIndex = i;
for(var j = i+1;j<len;j++){
// 寻找最小的数
if(arr[j]<arr[minIndex]){
minIndex = j;
}
}
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
console.log(arr,'beforeSort');
selectSort(arr);
console.log(arr,'afterascSort');
3.3、动图描述
四、快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
4.1、算法描述
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
1、从数列中挑出一个元素,称为 “基准”(pivot);
2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
4.2、代码实现
var quickSort = function(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));
};