排序
- 排序分为内部排序和外部排序。内部排序只使用内存,外部排序使用内存+外存,先加载一部分进行排序再加载一部分
- 内部排序又分为冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序和堆排序
- 时间复杂度
-
衡量算法程序的执行时间
-
时间频度
- 可以忽略常数项
- 可以忽略低次项
- 可以忽略系数
-
常见算法时间复杂度
-
O(1)<O(logn)<O(n)<O(nlogn)<O(n²)<O(n³)<O(2ⁿ)
-
常数阶O(1)
- 无论代码执行多少行只要没有复杂的循环结构时间复杂度都是O(1)
var i=1,j=2; ++i;j++; var m=i+j;
-
对数阶O(logn)
var i=0,n=50; while(i<n){ i =i*2; }
-
线性阶O(n)
for(let i=0;i<n;i++){ console.log("0"); }
-
线性对数阶
for(let i=0;i<n;i++){ var j=1; while(j<n){ j =j*2; } }
-
-
平方阶O(n²) 双重for循环
-
立方阶、k次方阶多重for循环
-
平均时间复杂度、最坏时间复杂度
-
冒泡排序
- 从左到右,一次比较相邻元素的大小
- 实现思路
- 共进行数组长度减一次循环
- 每一趟比较次数递减
- 可以进行优化,若某次排序未发生交换即可提前结束
- 冒泡排序图示(3 9 -1 10 20)
- 代码实现
var arr = [-5,5,3,0,8,-9];
function bubbleSort(arr){
var flag=false;
for(let i=0;i<arr.length-1;i++){
flag=false;
for(let j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){//如果前一个比后一个大则交换
let temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=true;
}
}
if(flag==false){
break;
}
}
}
bubbleSort(arr);
console.log(arr);
选择排序
- 选取某个元素,与后面的元素中寻找是否比它小(或大)的,若有则进行交换
- 实现思路
- 选择排序共要进行数组长度减一次排序
- 每次排序先假定这个数是最小数,然后依次和后面的元素进行比较,发现有更小的数就记录值和下标,直至扫描完,交换
- 选择排序图示
- 代码实现
var arr = [8,3,2,1,7,4,6,5];
function selectSort(arr){
var index;
var value;
for(let i=0;i<arr.length-1;i++){
index=i;
value=arr[i];
for(let j=i+1;j<arr.length;j++){
if(arr[j]<value){
index=j;
value=arr[j];
}
}
let temp = arr[index];
arr[index]=arr[i];
arr[i]=temp;
}
}
selectSort(arr);
console.log(arr);
插入排序
-
将要排序的数组依次插入临时的数组中,并且插入时要比较该元素与临时数组中元素的大小
-
插入排序图示
-
代码实现
var arr=[8,3,2,1,7,4,6,5];
function insert(num,arr){
var i;
for(i=0;i<arr.length;i++){
if(arr[i]>=num){
break;
}
}
if(i<arr.length){//证明数组中有元素比num大,num可以插入到数组中
arr.push(arr[arr.length-1]);//将最后一个元素往后推一个
for(let j=arr.length-2;j>=i;j--){
arr[j+1]=arr[j];
}
arr[i]=num;
}else{
arr.push(num);//将元素放在数组最后
}
}
function insertSort(arr){
var arr1 = [];
arr1[0]=arr[0];
for(let i=1;i<arr.length;i++){
insert(arr[i],arr1);
}
return arr1;
}
var arr1=insertSort(arr);
console.log(arr1);
希尔排序
- 希尔排序是更高效的插入排序
- 处理方式
- 把记录按下表的一定增量分组,对每组进行插入算法排序随着增量减小,每组包含的值越来越多,当增量变为1时,整个数组变成一个,算法终止
- 希尔排序图示
- 代码实现
var arr=[8,3,2,1,7,4,6,5,9,0];
function shellSort(arr){
var step =arr.length;
while(step){
for(let i=step;i<arr.length;i++){
for(let j=i-step;j>=0;j-=step){
if(arr[j]>arr[j+step]){
let temp = arr[j];
arr[j]=arr[j+step];
arr[j+step]=temp;
}
}
}
step=parseInt(step/2);
}
}
shellSort(arr);
console.log(arr)
但是这样实现,仍然存在问题,这样实现的希尔排序交换次数过多,可能会导致比插入排序还要浪费时间,所以,有了以下的代码优化
var arr=[8,3,2,1,7,4,6,5,9,0];
function shellSort(arr){
var step =arr.length;
while(step){
for(let i=step;i<arr.length;i++){
let j=i;
let value=arr[i];
if(arr[j]<arr[j-step]){
while(j-step>=0&&value<arr[j-step]){
arr[j]=arr[j-step];
j-=step;
}
arr[j]=value;
}
}
step=parseInt(step/2);
}
}
shellSort(arr);
console.log(arr)
快速排序
- 对冒泡排序的改进
- 实现思路
- 通过一趟排序将要排序的数据分隔为独立的两部分,其中一部分的数据比比某个数据都要小,另一部分比某个数据都要大,然后根据此方法进行递归,直至数组被排完序
- 快速排序的图示
- 代码实现
var arr=[8,3,2,1,7,4,6,5,9,0];
function quickSort(arr,left,right){
var l=left;
var r=right;
var index=parseInt((l+r)/2);
var center=arr[index];
while(l<r){
while(arr[l]<center){l++;}
while(arr[r]>center){r--;}
if(l>=r){break;}//证明左边已经都小于center,右边也都已经都大于center了
let temp = arr[l];
arr[l]=arr[r];
arr[r]=temp;
if(arr[l]==center){r--;}
if(arr[r]==center){l++;}
}
if(l==r){
l++;r--;
}
if(left<r){quickSort(arr,left,r);}
if(l<right){quickSort(arr,l,right);}
}
quickSort(arr,0,arr.length-1)
console.log(arr)
另一种思路,以第一个数据为参照
var arr=[8,3,2,1,7];
function quickSort(arr,start,end){
var key;
var k;
var i,j;
i = start;
j = end;
key=arr[i];//以第一个数为基准,将该数组分为小于它的一部分,和大于它的一部分
while(i<j)
{
while(i<j&&arr[j]>key) j--;
arr[i]=arr[j];
while(i<j&&arr[i]<=key) i++;
arr[j]=arr[i];
}
arr[i]=key;
k = i;
//start位置为基准,对其左右进行递归
if(start<k) {
quickSort(arr,start,k-1);
}
if(end>k) {
quickSort(arr,k+1,end);
}
}
quickSort(arr,0,arr.length-1)
console.log(arr)
归并排序
- 采用分治策略,将大问题分为小问题,先解决小问题再递归解决大问题
- 归并排序图示
- 代码实现
var arr=[8,3,2,1,7,4,6,5,9,0];
function merge(arr,left,right,mid){
var i=left;
var j=mid+1;
var temp=[];
var t=0;
while(i<=mid&&j<=right){
if(arr[i]<=arr[j]){
temp[t++]=arr[i++];
}else{
temp[t++]=arr[j++];
}
}
while(i<=mid){
temp[t++]=arr[i++];
}
while(j<=right){
temp[t++]=arr[j++];
}
var templeft=left;
for(let i=0;i<t;i++){
arr[templeft++]=temp[i];
}
}
function mergeSort(arr,left,right){
if(left<right){
var mid = parseInt((left+right)/2);
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,right,mid);
}
}
mergeSort(arr,0,arr.length-1);
console.log(arr);
基数排序
- 基数排序是桶排序的扩展
- 基数排序是效率高的稳定的排序方法,经典的空间换时间
- 基本思想
- 将所有待比较的数统一为同样地长度,数位较短的前面补零,然后从低位开始一次桶排序
- 基数排序图示
- 代码实现
var arr=[53,3,542,748,14,214];
function radixSort(arr){
var tempArr=new Array(10);
for(let i=0;i<10;i++){
tempArr[i]=[];
}
var max = arr[0];
for(let i=1;i<arr.length;i++){
if(arr[i]>max){
max=arr[i];
}
}
max=max+"";
var times=max.length;
// var times=0;
// while(max){
// max=parseInt(max/10);
// times++;
// }
var tens=1;
while(times--){
for(let i=0;i<10;i++){
tempArr[i]=[];
}
for(let i=0;i<arr.length;i++){
let index = parseInt(arr[i]/tens)%10;
tempArr[index].push(arr[i]);
}
tens*=10;
var t=0;
for(let i=0;i<10;i++){
for(let j=0;j<tempArr[i].length;j++){
arr[t++]=tempArr[i][j];
}
}
}
console.log(arr)
}
radixSort(arr);
堆排序
- 堆排序是一种不稳定的选择排序
- 基本思想
- 将无序序列构建为一个堆,根据升序或者降序选择大顶堆或小顶堆
- 将堆顶元素与末尾元素交换,将最大元素放在数组末端
- 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与末尾元素,反复执行调整和交换,直到整个序列有序
- 图示
- 代码实现
var arr = [4,6,8,5,9,50,-5,5];
function heapSort(arr){
for(let i=parseInt(arr.length/2)-1;i>=0;i--){
adjustArr(arr,i,arr.length);
}
for(let i=arr.length-1;i>0;i--){
let temp = arr[i];
arr[i]=arr[0];
arr[0]=temp;
adjustArr(arr,0,i);
}
}
function adjustArr(arr,i,length){
var temp = arr[i];
for(let k=2*i+1;k<length;k=k*2+1){
if(k+1<length&&arr[k+1]>arr[k]){
k++;
}
// if(arr[i]<arr[k]){
if(temp<arr[k]){
arr[i]=arr[k];
i=k;
}else{
break;
}
}
arr[i]=temp;
}
heapSort(arr);
console.log(arr)