时间复杂度为O(n^2)的:
插入、选择(不稳定)、冒泡
时间复杂度为O(nlogn)的:
堆(不稳定),快速(不稳定),归并
一、插入排序
将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过为止。
它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
public static void 插入排序(int[] arr) { for(int i=0;i<arr.length-1;i++) { for(int j=i+1;j>0;j--) { if(arr[j-1]>arr[j]) { swap(arr,j-1,j); } } } System.out.println(Arrays.toString(arr)); }
O(n^2) 稳定
二、选择排序
每次都在剩余的数组中找到最小值,把找到的最小值和头节点值交换,重复arr.length轮
最坏情况每次都必须换一下,都坏O(n^2)
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
public static void 选择排序(int[] arr) { for(int i=0;i<arr.length-1;i++) { int minIndex=i; for(int j=i+1;j<arr.length;j++) { if(arr[j]<arr[minIndex]) { minIndex=j; } } if(minIndex != i) { swap(arr,minIndex,i); } } System.out.println(Arrays.toString(arr)); }
O(n^2), 不稳定
序列5 8 5 2 9, 这个在执行选择排序的时候,第一遍,肯定会将array[0]=5,交换到2所在的位置,也就是 2 8 5 5 9,那么很显然,之后的排序我们就会发现,array[2]中的5会出现在原先的array[0]之前,所以选择排序不是一个稳定的排序
三、冒泡排序
不解释,你学的第一个算法
public static void 冒泡排序(int[] arr) { for(int i=arr.length-1;i>0;i--) //每次需要排序的长度 { for(int j=0;j<i;j++) { if(arr[j]>arr[j+1]) swap(arr,j,j+1); } } System.out.println(Arrays.toString(arr)); }
O(n^2) , 稳定
四、快速排序
递归的思想,每次选择一个元素,左边的数组都小于选定元素,右边的数组都大于选定元素;
public static void 快速排序(int[] arr,int low,int high) { if(arr.length<=0) return; if(low>=high) return; int left = low; int right = high; int temp = arr[low]; while(left<right) { while(left<right && arr[right]>temp) right--; while(left<right && arr[left]<=temp) left++; swap(arr,left,right); } swap(arr,left,low); System.out.println("Sorting:" + Arrays.toString(arr)); 快速排序(arr,low,left-1); 快速排序(arr,left+1,high); }
https://www.youtube.com/watch?v=T258-Umoz9A
O(nlogn) 不稳定
五、归并排序
递归版本:
public static void mergeSort1(int[] arr,int l, int r) { if(l<r) { int m=l+(r-l)/2; mergeSort1(arr,l,m); mergeSort1(arr,m+1,r); merge(arr,l,m,r); } } public static void merge(int[] arr,int l,int m,int r) { int n1 = m-l+1; //左子数组的长度 int n2=r-m; //右子数组的长度 int[] L=new int[n1]; int[] R=new int[n2]; for(int i=0;i<n1;i++) L[i] = arr[l+i]; for(int j=0;j<n2;j++) R[j] = arr[m+1+j]; int i=0,j=0,k=l; while(i<n1 && j<n2) { if(L[i]<=R[j]) { arr[k] = L[i]; i++; }else{ arr[k]=R[j]; j++; } k++; } while(i<n1) { arr[k] = L[i]; k++; i++; } while(j<n2) { arr[k] = R[j];k++; j++; } }
O(nlogn) 稳定
六、堆排序
堆排序的思想跟选择排序类似,但是如何快速找到最小(最大)元素?——》最大最小堆
public static void heapify(int[] arr,int n,int i) { int largest = i; int l=2*i+1;//左子树的位置 int r=2*i+2;//右子树的位置 if(l<n && arr[l]>arr[largest]) largest=l; if(r<n && arr[r]>arr[largest]) largest=r; if(largest != i) { swap(arr,i,largest); heapify(arr,n,largest); } } public static void 堆排序(int[] arr) { int n=arr.length; for(int i=n/2-1;i>=0;i--) heapify(arr,n,i); for(int i=n-1;i>=0;i--) { swap(arr,0,i); heapify(arr,i,0); } }
O(nlogn) 不稳定
堆分为大顶堆和小顶堆;大顶堆:父节点的值一定大于它的左右节点的值。
堆排序的过程:
1.构造一个大顶堆,取堆顶数字;
2. 再将剩下的数字构造一个大顶堆,取堆顶的数字
3.重复上面的操作,直到取完堆中的数字,最终得到一个从大到小排序的序列