排序算法
文章目录
冒泡排序 O(n^2)
冒泡排序比较所有相邻的两个项,如果第一个比第二个大,则交换它们。元素项向上移动至正确的顺序,就好像气泡升至表面一样,冒泡排序因此得名。
// 冒泡排序
function bubbleSort(arr){
const {length} = arr
for(let i=0;i<length;i++){
for(let j=0;j<length-1;j++){
if(arr[j]>arr[j+1]){
let temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
}
return arr
}
这个生成随机数组的函数待会还会用
//生成一定数量随机数组的方法
function createNonSortedArray(size) {
const array = [];
for (let i = size; i > 0; i--) {
array.push(Math.floor(Math.random()*10));
}
return array;
}
let array = createNonSortedArray(8); // {7}
console.log(array.join()); // {8}
console.log(bubbleSort(array))
// "8,9,4,9,6,5,7,2"
// [2,4,5,6,7,8,9,9]
选择排序 O(n^2)
选择排序算法是一种原址比较排序算法。选择排序大致的思路是找到数据结构中的最小值并将其放置在第一位,接着找到第二小的值并将其放在第二位,以此类推。
//选择排序
function selectionSort(arr){
const { length } = arr
let minIndex
for(let i=0;i<length-1;i++){
minIndex = i
for(let j=i;j<length;j++){
if(arr[minIndex]>arr[j]){
minIndex = j
}
}
if(i !== minIndex){
let temp = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
}
return arr
}
插入排序 O(n^2)
插入排序每次排一个数组项,以此方式构建最后的排序数组。假定第一项已经排序了。接着,它和第二项进行比较——第二项是应该待在原位还是插到第一项之前呢?这样,头两项就已正确排序,接着和第三项比较(它是该插入到第一、第二还是第三的位置呢),以此类推。
// 插入排序
function insertSort(arr){
const {length} = arr
//将第一个元素标记为已排序
for(let i=1;i<length;i++){
//对于后面每个未排序的元素提取数据
let temp = arr[i]
let j = i
while(j>0 && arr[j-1]>temp){
arr[j] = arr[j-1] //如果前面的值大于temp值 所有元素后移 给temp让地方
j-- //不断往前比较
}
arr[j]=temp //插入在这儿
}
return arr
}
归并排序 O( nlog(n) )
归并排序是一种分而治之算法。其思想是将原始数组切分成较小的数组,直到每个小数组只有一个位置,接着将小数组归并成较大的数组,直到最后只有一个排序完毕的大数组。归并排序是递归的。
function mergeSort(arr){
//判断如果已经划分到最小 [x] 就不再分
if(arr.length > 1){
const {length} =arr
let middle = Math.floor(length/2)
// 继续利用递归分化成两半
let left = mergeSort(arr.slice(0,middle))
let right = mergeSort(arr.slice(middle,length))
arr = merge(left,right)
}
return arr
}
function merge(left,right){
let i = 0
let j = 0
const result = []
//循环直到其中一个指针抵达length,说明对比完毕 合成一个大块
while (i<left.length && j<right.length){
if(left[i]<right[j]){
result.push(left[i])
i++
}else{
result.push(right[j])
j++
}
}
return result.concat(i < left.length ? left.slice(i):right.slice(j))
// 这段代码是指 如果i还小于左侧length 说明j=right.length到头了 直接把left从i往后剩下的拼接到数组里,反之则是i=left.length到头了,把right这边拼接过去
}
*快速排序 O(nlog(n))
快速排序也许是最常用的排序算法了。它的复杂度为O(nlog(n)),且性能通常比其他复杂度为O(nlog(n))的排序算法要好。和归并排序一样,快速排序也使用分而治之的方法,将原始数组分为较小的数组(但它没有像归并排序那样将它们分割开)。
function quickSort(arr) {
// 如果数组长度小于等于1,直接返回
if (arr.length <= 1) {
return arr;
}
// 选择数组中间元素作为基准
let pivot = arr[Math.floor(arr.length / 2)];
// 定义两个空数组存放左右两部分
let left = [];
let right = [];
// 遍历数组,将比基准小的元素放入左边数组,将比基准大的元素放入右边数组
for (let i = 0; i < arr.length; i++) {
if (i === Math.floor(arr.length / 2)) {
continue; // 跳过基准元素
}
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 对左右两部分递归地进行快速排序,并拼接起来返回
return quickSort(left).concat(pivot, quickSort(right));
}
计数排序 O(n+k)
计数排序是一个分布式排序。分布式排序使用已组织好的辅助数据结构,然后进行合并,得到排好序的数组。计数排序使用一个用来存储每个元素在原始数组中出现次数的临时数组。在所有元素都计数完成后,临时数组已排好序并可迭代以构建排序后的结果数组。
function countingSort(array) {
if (array.length < 2) { // {1}
return array;
}
const maxValue = findMaxValue(array); // {2}
const counts = new Array(maxValue + 1); // {3}
array.forEach(element => {
if (! counts[element]) { // {4}
counts[element] = 0;
}
counts[element]++; // {5}
});
let sortedIndex = 0;
counts.forEach((count, i) => {
while (count > 0) { // {6}
array[sortedIndex++] = i; // {7}
count--; // {8}
}
});
return array;
}
function findMaxValue(array) {
let max = array[0];
for (let i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
桶排序 O(n+k)
桶排序(也被称为箱排序)也是分布式排序算法,它将元素分为不同的桶(较小的数组),再使用一个简单的排序算法,例如插入排序(用来排序小数组的不错的算法),来对每个桶进行排序。然后,它将所有的桶合并为结果数组。
function bucketSort(array, bucketSize = 5) { // {1}
if (array.length < 2) {
return array;
}
const buckets = createBuckets(array, bucketSize); // {2}
return sortBuckets(buckets); // {3}
}
function createBuckets(array, bucketSize) {
let minValue = array[0];
let maxValue = array[0];
for (let i = 1; i < array.length; i++) { // {4}
if (array[i] < minValue) {
minValue = array[i];
} else if (array[i] > maxValue) {
maxValue = array[i];
}
}
const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; // {5}
const buckets = [];
for (let i = 0; i < bucketCount; i++) { // {6}
buckets[i] = [];
}
for (let i = 0; i < array.length; i++) { // {7}
const bucketIndex = Math.floor((array[i] - minValue) / bucketSize); // {8}
buckets[bucketIndex].push(array[i]);
}
return buckets;
}
function sortBuckets(buckets) {
const sortedArray = []; // {9}
for (let i = 0; i < buckets.length; i++) { // {10}
if (buckets[i] ! = null) {
insertionSort(buckets[i]); // {11}
sortedArray.push(...buckets[i]); // {12}
}
}
return sortedArray;
}
基数排序 O(n+k)
基数排序也是一个分布式排序算法,它根据数字的有效位或基数(这也是它为什么叫基数排序)将整数分布到桶中。基数是基于数组中值的记数制的。
比如,对于十进制数,使用的基数是10。因此,算法将会使用10个桶用来分布元素并且首先基于个位数字进行排序,然后基于十位数字,然后基于百位数字,以此类推。
function radixSort(array, radixBase = 10) {
if (array.length < 2) {
return array;
}
const minValue = findMinValue(array);
const maxValue = findMaxValue(array);
let significantDigit = 1; // {1}
while ((maxValue - minValue) / significantDigit >= 1) { // {2}
array = countingSortForRadix(array, radixBase, significantDigit, minValue); // {3}
significantDigit *= radixBase; // {4}
}
return array;
}
function countingSortForRadix(array, radixBase, significantDigit, minValue) {
let bucketsIndex;
const buckets = [];
const aux = [];
for (let i = 0; i < radixBase; i++) { // {5}
buckets[i] = 0;
}
for (let i = 0; i < array.length; i++) { // {6}
bucketsIndex = Math.floor(((array[i] - minValue) / significantDigit) %
radixBase); // {7}
buckets[bucketsIndex]++; // {8}
}
for (let i = 1; i < radixBase; i++) { // {9}
buckets[i] += buckets[i -1];
}
for (let i = array.length -1; i >= 0; i--) { // {10}
bucketsIndex = Math.floor(((array[i] - minValue) / significantDigit) %
radixBase); // {11}
aux[--buckets[bucketsIndex]] = array[i]; // {12}
}
for (let i = 0; i < array.length; i++) { // {13}
array[i] = aux[i];
}
return array;
}