之前把常见的那几种算法看了好几次,看的时候也手写了一遍,可是等到过了
好几天又给忘了,所以决定记录下来分享一下,也方便自己以后查看?
冒泡排序
冒泡排序是属于交换排序的一种,通过比较相邻的两个数据然后进行交
换,冒泡排序的时间复杂度为o(n^2),空间复杂度为o(1)。
function BubbleSort(arr) {
for(let i=0;i<arr.length;i++){
for(let j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
[arr[j],arr[j+1]]=[arr[j+1],arr[j]]; //交换值
}
}
}
return arr;
}
选择排序
选择排序是通过记录下标的方式进行比较的,定义变量(index)等于当前
下标(i),循环当前下标后面的元素,如果后面的元素比当前元素大(或者小),先不着急
交换元素,只替换下标(下标交换),一次循环过后最小元素下标的位置可以确定,最
终交换当前元素(arr[i])值和最小(或最大)下标(arr[index])的值。选择排序的时间复杂
度为o(n^2),空间复杂度为o(1)。
// 选择
function chooseSort(arr){
for(let i = 0;i<arr.length;i++){
let index = i; //记录当前元素下标
for(let j =i+1;j<arr.length;j++){
if(arr[j]<arr[index]){
index = j; //只交换下标
}
}
[arr[i],arr[index]] = [arr[index],arr[i]] //一次循环之后再进行交换
}
return arr
}
插入排序
插入排序默认数组第一个元素有序,从第二个元素开始从后向前进行
比较,插入排序的时间复杂度为o(n^2)。
//插入排序
function insert(arr) {
for (let i = 1; i < arr.length; i++) {
let current = arr[i]; //记录当前值
let pre_index = i - 1; //得到上一个元素下标
while (current <= arr[pre_index] && pre_index >= 0) { //如果下标小于0,已经到达数组开头
arr[pre_index + 1] = arr[pre_index]; //交换元素
pre_index--; //再比较下一位元素
}
arr[pre_index + 1] = current;
}
return arr;
}
以上这几种的时间复杂度都是o(n^2),也比较耗费性能。下面介绍两种时间复
杂度为o(nlogn)的排序算法。
快速排序
我选择了这种方式的快排,比较好理解。还有一种 “刨坑式”的快排,那种在理
解上有点难度,它通过左右两个指针(或变量)对应的值进行比较,然后进行交换。
以下代码的原理是通过数组进行排序的,递归的去排序左右数组。
//快排
function quickSort(arr) {
if (arr.length <= 1) return arr;
let left = [],
middle = [arr[0]],
right = [];
for (let index = 1; index < arr.length; index++) {
if (arr[index] < middle[0]) {
left.push(arr[index]) //比它小的放到左边
}else {
right.push(arr[index]) //本身一样大的元素也放入右边
}
}
// 递归并连接,这一步很重要
return quickSort(left).concat(middle, quickSort(right))
}
归并排序
归并排序主要是利用分治算法的思想,将大问题化解成小问题,最终得到
原问题的解。递归地把数组分割成前后两个子数组,直到数组中只有1个元素。同
时,递归地从两个数组中挨个取元素,比较大小并合并。时间复杂度为o(nlogn),空间复杂
度为o(n)。
// 分割成只有一个元素的数组
function Split(arr){
if(arr.length<2) return arr;
let mid = Math.floor(arr.length/2); //取中间位置
let left = arr.slice(0,mid); //得到左边数组
let right = arr.slice(mid); //得到右边数组
return Merge(Split(left),Split(right));
}
function Merge(left,right){
// 合并+排序
var result = [];
var nl = 0;
var nr =0;
while(nl<left.length && nr<right.length){
if(left[nl] < right[nr]){//按从小到大的顺序排列新的组合数组
result.push(left[nl++]);//扔进去之后迭代
}else{
result.push(right[nr++]);
}
}
while(nl<left.length){//剩下的一股脑儿扔进去。
result.push(left[nl++]);
}
while(nr<right.length){
result.push(right[nr++]);
}
return result;
}
基数排序
function radixSort(arr) {
let max_num = Math.max(...arr);
max_len = getLengthOfNum(max_num);
//根据最大数进行循环(确定循环次数)
for (let digit = 1; digit <= max_len; digit++) {
let buckets = [];
for (let i = 0; i < 10; i++) buckets[i] = []; //定义“桶”
for (let i = 0; i < arr.length; i++) {
let value= arr[i];
let pos = getSpecifiedValue(value, digit);
buckets[pos ].push(value);
}
let result = [];
buckets.toString().split(',').forEach((val) => {
if (val) result.push(parseInt(val))
})
arr = result; //改变原数组,再次排序 重要
}
return arr;
}
function getLengthOfNum(num) { return (num += '').length } //得到最大数的长度
//根据对应位置的数,如果不存在,返回0
function getSpecifiedValue(num, position) { return (num += '').split('').reverse().join('')[position - 1] || 0 }
对于基数排序可以参考基数排序详解这篇博客,讲解的比较清晰。
稳定的排序方法:冒泡排序、插入排序、基数排序、归并排序
不稳定的排序方法:快速排序、选择排序、堆排序、希尔排序