常用排序算法
本文章算法实现皆使用js实现。
1. 冒泡排序
分析:原理如它名字一样,不断的让最大(最小)的数字冒出来,完成排序。时间复杂度为O(n²)。
算法实现:
let effervescent = (nums) => { let len = nums.length; for (let i = 0; i < len; i++) { for (let j = i + 1; j < len; j++) { if (nums[i] > nums[j]) { let temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } } } return nums; }
2. 选择排序
分析:遍历获取每次的最小(最大)值的下标,遍历完成后和边界位置置换,一直循环到全部数据,完成排序。 时间复杂度O(n²)。
算法实现:
let choose = (nums) => { let len = nums.length; for (let i = 0; i < len; i++) { let minIndex = i; for (let j = i + 1; j < len; j++) { if (nums[j] < nums[minIndex]) { minIndex = j; } } let temp = nums[minIndex]; nums[minIndex] = nums[i]; nums[i] = temp; } return nums; }
3. 快速排序
分析:选择一个基准元素,将数组中大于它的数放这个右边,小于它的数字放左边。然后将左边,右边递归调用这个方法进行排序。 时间复杂度 O(nlogn)。
算法实现
let quick = (nums, start = 0, end = 0) => { let len = nums.length; if(len < 2){ return nums; } let leftIndex = start, rightIndex = end === 0 && start === 0 ? len - 1 : end; if(rightIndex - leftIndex < 1){ return; } let flag = nums[start]; //基准 while (leftIndex < rightIndex) { while (leftIndex < rightIndex) { //从右边找出一个小于基准的数字,放入左边位置 if (flag > nums[rightIndex]) { nums[leftIndex++] = nums[rightIndex]; break; } --rightIndex; } while (leftIndex < rightIndex) { //从左边找出一个大于基准的数字,放入右边位置 if (flag <= nums[leftIndex]) { nums[rightIndex--] = nums[leftIndex]; break; } ++leftIndex; } } nums[leftIndex] = flag; //将基准放入中间位置,该位置为上面移位后空余的位置 quick(nums, start, leftIndex - 1);//left quick(nums, rightIndex + 1, end);//right return nums; }
4. 插入排序
分析:将数组看成两个部分,前面部分为排序部分,后面部分为待排序部分,一次遍历待排序部分,插入排序部分。排序部分不断增加,待排序部分不断减少,直至变成目标排序数组。时间复杂度O(n²)。
算法实现:
var insert = (nums) => { let len = nums.length; for (let i = 1; i < len; i++) { let target = nums[i]; //找出插入位置 let j = 0; while (j < i) { if (target < nums[j]) { break; } j++; } //插入 if (j < i) { let removeIndex = i; //后移 while (removeIndex >= j) { nums[removeIndex] = nums[removeIndex - 1]; removeIndex--; } //插入 nums[j] = target; } } return nums; }
5. 希尔排序
分析:设置插入比较的步长,不断进行插入排序,并且减少步长,他会逐渐使序列变得有序,当步长为1时,就是插入排序。它是插入排序的进阶版,主要优点是减少移动次数。时间复杂度O(n²)。
let shell = (nums) => { let len = nums.length; for (let step = Math.round(len / 2); step > 0; step = Math.floor(step / 2)) {//step 为分组长度 for (let i = step; i < len; i += step) { let flag = nums[i]; //找到插入位置 let j = 0; while (j < i) { if (nums[j] > flag) { break; } j += step; } //后移 if (j < i) { let removeIndex = i; while(removeIndex > j){ nums[removeIndex] = nums[removeIndex - step]; removeIndex -= step; } nums[j] = flag; } } } return nums; }
6. 桶排序
分析:获取数组中最大值, 用该值初始化一个长度为该值的一个数组(桶),初始各项为0,遍历需要排序的数组,把每个出现的数字作为下标,标记桶数组该下标的数量。最后遍历桶,输出内容大于0的下标,内容有多少就输出多少次。这样做的缺点很明显,在出现一个特别大的数字情况下特别的浪费空间,不过在特定情况下,它的排序速度会比较快。时间复杂度为: O(x*n)。
let bucket = (nums) => { let arr = []; let len = nums.length; //获取最大值 let max = nums[0]; let i = 1; while(i < len){ if(nums[i] > max){ max = nums[i]; } i++; } //初始化桶 let bucketArr = new Array(max); //标记桶 i = 0; while(i < len){ let val = bucketArr[nums[i]]; if(val && val > 0){ bucketArr[nums[i]] ++; }else{ bucketArr[nums[i]] = 1; } i++; } //得到数组 i = 0; while(i <= max){ if(bucketArr[i] && bucketArr[i] > 0){ let j = 0; while(j < bucketArr[i]){ arr.push(i); j++; } } i ++; } return arr; }
7. 基数排序
分析:升级版本的桶排序。低位排序(LSD):初始化一个长度为10的数组(桶)。首先对待排序的个位数进行桶排序,获取排序结果。然后对结果进行十位数的桶排序。直至完成最大位数的桶排序,获取最后的排序结果。高位排序(MSD): 初始化一个长度为10的数组(桶)。首先对最高位进行桶排序,然后再对桶内的元素进行低一位的排序,一直到个位完成排序。两种方式不同之处在于,高位排序为真实排序,所以不必再和其它为进行比对。 时间复杂度: O(x*(log(r))*n)。
LSD算法实现为:
let radixLsd = (nums) => { let len = nums.length; let bucketArr = new Array(10); //找到最大位,确定排序次数 let max = nums[0]; for (let i = 1; i < len; i++) { if (nums[i] > max) { max = nums[i]; } } let maxLen = max.toString().length; let index = 0; //[0, maxLen) while (index < maxLen) { bucketArr = new Array(10); //对该位桶排序 let i = 0; // 遍历nums [0, len) //入桶 while (i < len) { //获取nums[i] index位置的数字 let valArr = nums[i].toString().split(''); let numberIndex = valArr.length <= index ? -1 : valArr.length - index - 1; let number = numberIndex >= 0 ? parseInt(valArr[numberIndex]) : 0; if (bucketArr[number]) { bucketArr[number].push(nums[i]); } else { bucketArr[number] = [nums[i]]; } i++; } nums = []; //出桶 let j = 0; //遍历桶的索引 [0, 9] while (j <= 9) { if (bucketArr[j]) { for (let k = 0; k < bucketArr[j].length; k++) { nums.push(bucketArr[j][k]); } } j++; } index++; } return nums; }
MSD算法实现为:
let radixMsd = (nums) => { //递归获取排序桶 let bucketArr = bucketMsd(nums, -1); //递归读取桶中排序数据 let arr = []; readBucket(bucketArr, arr); return arr; } let readBucket = (bucket, arr) => { if (bucket instanceof Array) { for (let i = 0; i < bucket.length; i++) { if (bucket[i]) { readBucket(bucket[i], arr); } } } else { if (typeof(bucket) === 'number') { arr.push(bucket); } } } //对数组高位桶排序,返回桶 let bucketMsd = (nums, index) => { //index 为排序位,-1时为最高位置 let len = nums.length; let bucketArr = new Array(10); if (index === -1) { //找到最大位,确定排序次数 let max = nums[0]; for (let i = 1; i < len; i++) { if (nums[i] > max) { max = nums[i]; } } let maxLen = max.toString().length; index = maxLen - 1; //控制桶排序次数 [0, max - 1] } //根据最高位入桶 for (let i = 0; i < len; i++) { //获取nums[i] index位置的数字 let valArr = nums[i].toString().split(''); let numberIndex = valArr.length <= index ? -1 : valArr.length - index - 1; let number = numberIndex >= 0 ? parseInt(valArr[numberIndex]) : 0; if (bucketArr[number]) { bucketArr[number].push(nums[i]); } else { bucketArr[number] = [nums[i]]; } } if (index > 0) { for (let k = 0; k < bucketArr.length; k++) { if (bucketArr[k] && bucketArr[k].length > 0) { bucketArr[k] = bucketMsd(bucketArr[k], index - 1); } } } return bucketArr; }