O(n^2)以及O(nlogn)时间复杂度的排序算法
Posted BarneyWhite
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了O(n^2)以及O(nlogn)时间复杂度的排序算法相关的知识,希望对你有一定的参考价值。
都是做的升序。
思路:每次选择还未排序的区间的最小值和未排序区间的第一个值交换。
1 function selectSort(arr){ 2 for(let i = 0; i < arr.length; i++){ 3 let minIdx = i; 4 for(let j = i; j < arr.length; j++){ 5 if(arr[j] < arr[minIdx]){ 6 minIdx = j; 7 } 8 } 9 let tmp = arr[i]; 10 arr[i] = arr[minIdx]; 11 arr[minIdx] = tmp; 12 } 13 return arr; 14 }
思路:当前位置的值与前面排好序的区间从后往前对比,找到适合的插入位置并插入。
适用于:近乎有序的排序,在几乎有序的情况下,它的速度会比n(logn)的算法还要快,可以接近O(n),插入排序要优于选择排序
1 function insertSort(arr){ 2 let tmp; 3 for(let i = 0; i < arr.length; i++){ 4 for(let j = i - 1; j >= 0; j--){ 5 if(j === 0){ 6 if(arr[i] < arr[j]){ 7 // 在顺序表中,应该使用副本复制当前的值,采用赋值去修改数组,而不是删除和插入,因为在顺序表中删除和插入的时间复杂度是n 8 tmp = arr.splice(i,1)[0]; 9 arr.splice(j,0,tmp); 10 } 11 }else if(arr[i] < arr[j] && arr[i] >= arr[j - 1]){ 12 tmp = arr.splice(i,1)[0]; 13 arr.splice(j,0,tmp); 14 } 15 } 16 // 做一个当前函数是否有序的判断 17 if(isSort(arr)){ 18 return arr; 19 } 20 } 21 return arr; 22 }
O(nlogn)的算法
归并排序
优化:在待排区间的长度小于100时,可以用插入排序
1 // 自下而上的归并算法,自上而下需要用到递归 2 function mergeSort(arr){ 3 let arrSort = [],n = arr.length,count = 0; 4 for(let size = 1; size <= n; size += size){ // size表示当前有序区间的长度 5 for(let i = 0; i < n; i += size*2){ 6 // 将[i...i+size-1]和[i+size...i+size*2-1]的两个区间融合成一个有序的并添加到arrSort后面 7 arrSort = arrSort.concat(orderMerge(arr.slice(i,i+size),arr.slice(i+size,((i+size*2) > n? n : (i+size*2))))); 8 } 9 arr = arrSort.slice(0); 10 arrSort.length = 0; 11 } 12 return {arr,count}; 13 // orderMerger 函数主要是讲有序的两个表融合成一个有序表 14 function orderMerge(arr1,arr2){ 15 let arr = []; 16 let idx1 = 0, idx2 = 0; 17 while(idx1 < arr1.length && idx2 < arr2.length){ 18 if(arr1[idx1] <= arr2[idx2]){ 19 arr.push(arr1[idx1++]); 20 }else{ 21 // 当arr1[index1] > arr2[idx2]的时候就是一个逆序对 22 arr.push(arr2[idx2++]); 23 count++; 24 } 25 } 26 if(idx1 === arr1.length){ 27 arr = arr.concat(arr2.slice(idx2)); 28 }else{ 29 arr = arr.concat(arr1.slice(idx1)); 30 } 31 return arr; 32 } 33 }
思想:选择待排序的区间中第一个p数作为参照,大于p的放在p的后面,小于p的放前面。最后将p放在两个区间的中间,递归下去。
缺点:
: 1 在排近乎有序的数组的时候,时间复杂度趋近于O(n^2)
2 再相同数值很多的数组中,时间复杂度趋近于O(n^2)
解决方案
: 优化缺点1:随机选择参照数p或者待排数组中间的数作为参照数
优化缺点2-1 :从待排数组两边进行遍历,从左向右遇到>=p的值停顿,在从右向左遇到<=p的值停顿,将两个数交换并i++,j--,知道i == j为止
优化缺点2-2:三路快速排序,有三个数组,一个数组存放小于p的值,一个存放大于p的值,一个存放等于p的值。
1 function quickSort(arr){ 2 // 终止条件:当数组长度小于等于1是返回该数组 3 if(arr.length <= 1){ 4 return arr; 5 } 6 // 定义leftSort、rightSort存放小于和大于p的数 7 let leftSort = [], rightSort =[], midSort = [], odd = 0; 8 // 遍历arr,将大于p的放在rightSort中,小于p的放在leftSort中 9 //优化缺点1:选择中间的数做参照并把该数从原数组中删除 10 let idx = Math.floor(arr.length/2); 11 let p = arr.splice(idx,1)[0]; 12 for(let i = 0; i < arr.length; i++){ 13 if(arr[i] > p){ 14 rightSort.push(arr[i]); 15 }else if(arr[i] < p){ 16 leftSort.push(arr[i]); 17 }else{ 18 // 优化缺点2 19 if(odd){ 20 leftSort.push(arr[i]); 21 odd = 1; 22 }else{ 23 rightSort.push(arr[i]); 24 odd = 0; 25 } 26 } 27 } 28 // 递归leftSort、rightSort 29 leftSort = quickSort(leftSort); 30 rightSort = quickSort(rightSort); 31 // 将leftSort、midSort、rightSort合并成一个数组并返回。 32 arr = leftSort.concat(p,rightSort) 33 return arr; 34 } 35 36 //优化缺点2-2 37 function quickSort(arr){ 38 if(arr.length <= 1){ 39 return arr; 40 } 41 let leftSort = [], rightSort =[], midSort = []; 42 let idx = Math.floor(arr.length/2); 43 let p = arr[idx]; // 可以不用删除了 44 for(let i = 0; i < arr.length; i++){ 45 if(arr[i] > p){ 46 rightSort.push(arr[i]); 47 }else if(arr[i] < p){ 48 leftSort.push(arr[i]); 49 }else{ 50 midSort.push(arr[i]); 51 } 52 } 53 arr = quickSort(leftSort).concat(midSort,quickSort(rightSort)) 54 return arr; 55 }
Merge Sort的思路求逆序对的个数
mergeSort第21行
Qiuck Sort求数组中第n大的数
1 function theNumberN(arr,n){ 2 3 let p,idx, 4 leftSort = [], 5 midSort = [], 6 rightSort = []; 7 while(arr.length > 1){ 8 idx = Math.floor(arr.length/2); 9 p = arr[idx]; 10 for(let i = 0; i < arr.length; i++){ 11 if(arr[i] < p){ 12 leftSort.push(arr[i]); 13 }else if(arr[i] > p){ 14 rightSort.push(arr[i]); 15 }else{ 16 midSort.push(arr[i]); 17 } 18 } 19 if(leftSort.length >= n){ 20 arr = leftSort.slice(0); 21 } 22 23 else if(leftSort.length+midSort.length >= n){ 24 25 return midSort[0]; 26 } 27 28 else{ 29 30 arr = rightSort.slice(0); 31 n = n - leftSort.length - midSort.length; 32 } 33 leftSort.length = midSort.length = rightSort.length = 0; 34 35 } 36 return arr[0]; 37 38 }
或者使用递归
1 function theNumberN(arr,n){ 2 if(arr.length <= 1){ 3 return arr[0]; 4 } 5 // 选一个基点p 6 let p, 7 leftSort = [], 8 midSort = [], 9 rightSort = []; 10 // 小于p放leftSort,等于p放midSort,大于放rightSort 11 p = arr[Math.floor(arr.length/2)]; 12 for(let i = 0; i < arr.length; i++){ 13 if(arr[i] < p){ 14 leftSort.push(arr[i]); 15 }else if(arr[i] > p){ 16 rightSort.push(arr[i]); 17 }else{ 18 midSort.push(arr[i]); 19 } 20 } 21 // 如果leftSort.length>n则抛弃midSort和rightSort再找leftSort中第n大的数 22 if(leftSort.length >= n){ 23 theNumberN(leftSort,n); 24 } 25 // 否则判断leftSort.length+midSort.length>n则抛弃leftSort和rightSort,在midSort中找第n-leftSort.length大的数 26 else if(leftSort.length+midSort.length >= n){ 27 // theNumberN(midSort,n - leftSort.length); 28 return midSort[0]; 29 } 30 // 否则抛弃leftSort和midSort,在rightSort中找第n-leftSort.length-midSort.length大的数 31 else{ 32 theNumberN(rightSort,n - leftSort.length - midSort.length); 33 } 34 }
以上是关于O(n^2)以及O(nlogn)时间复杂度的排序算法的主要内容,如果未能解决你的问题,请参考以下文章
排序算法时间复杂度O(n^2)冒泡排序选择排序插入排序时间复杂度O(nlogn)快速排序堆排序归并排序其他排序希尔排序计数排序