排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
简单选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 |
直接插入排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
希尔排序 | O(nlogn) | O(n) | O(n2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n2) | O(logn)~O(n) | 不稳定 |
1 插入排序
1.1 直接插入排序
原理:把待排序元素看作一个有序数列和一个无序数列(开始时,有序数列只有一个元素),依次将无序数列中的元素与有序数列中的元素按照从后向前的顺序依次进行比较,从而找到合适的插入位置。
当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已排好序,此时按照由后向前的顺序,将array[i]与array[i-1],array[i-2],…,array[0]依次进行比较,找到合适的插入位置将array[i]插入,原来位置上的元素则顺序后移。
private static int[] insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
//若当前元素小于前一个元素,则需要插入合适的位置
if (arr[i] < arr[i-1]){
//按照从后向前的顺序,比较寻找合适的插入位置
int insertnum=arr[i];
int j=i-1;
while (j >= 0 && arr[j] > insertnum) {
arr[j+1]=arr[j];//元素向后移动一位
j--;//从后向前
}
arr[j+1]=insertnum;//插入元素
}
}
return arr;
}
时间复杂度:
最好情况:即待排序序列已经有序,此时每趟操作只需进行1次比较 ,而不需进行移动。此时有:总比较次数=n-1次;总移动次数=0次。
最坏情况:即待排序序列按逆序排序,此时在第i趟操作中,需要进行i次比较(和前面的i个关键字依次比较),i+1次移动。
此时:总比较次数=1+2+...+n-1=n(n-1)/2
总移动次数:2+3+...+n=(n+2)(n-1)/2
1.2 希尔排序
private static int[] ShellSort(int[] arr) {
int in = arr.length/2; //增量
while (in > 0) {
for (int i = in; i < arr.length; i++){
int sortnum=arr[i];
int j=i-in;
while(j>=0 && arr[j]>sortnum){
arr[j+in]=arr[j];
j=j-in;
}
arr[j+in]=sortnum;
}
in=in/2; //增量
}
return arr;
}
2 交换排序
2.1 冒泡排序
private static int[] bubbleSort(int[] arr) {
boolean flag=true;//有交换发生flag=true,没有则flag=false;没有交换发生,意味着元素已正序排列
for (int i = 0; i < arr.length && flag; i++) { //若flag=false,则退出循环
flag=false;//在第i趟中,还未发生过交换
for (int j = arr.length-1; j > i ; j--) {
if (arr[j] < arr[j-1]) {
int tmp = arr[j - 1];
arr[j-1] = arr[j];
arr[j] = tmp;
flag = true;//发生了交换,flag=true
}
}
}
return arr;
}
2.2 快速排序
原理:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分小于基准元素,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分(分治思想)
public static void quickSort(int[] arr, int start, int end) {
if (start>=end)
return; //递归终止条件
int index = getIndex(arr,start,end);//计算基准元素/中间元素位置
quickSort(arr,start,index-1);//对左侧子数组排序
quickSort(arr,index+1,end);//对右侧子数组排序
}
private static int getIndex(int[] arr, int start, int end) {
int temp = arr[start]; //中间位置元素
//从数组的两端交替向中间扫描
while (start<end){
//右侧开始判断,中间位置元素小于等于右侧,end--
while (start<end && temp<=arr[end]){
end--;
}
arr[start] = arr[end]; //小于基准元素的元素左移
//左侧开始判断,中间位置元素大于等于左侧元素,start++
while (start<end && temp>=arr[start]){
start++; //大于基准元素的元素又移
}
arr[end] = arr[start];
}
arr[start] = temp;
return start; //返回中间元素位置
}
3 选择排序
3.1 简单选择排序
private static int[] selectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int min=i;
int j=i+1;
while(j < arr.length) {
if (arr[j]<arr[min]){ //找到从arr[i]到arr[arr.length-1]中最小的元素
min=j;
}
j++;
}
if (i!=min) { //将找到的最小的元素移动到arr[i]
int tmp=arr[min];
arr[min]=arr[i];
arr[i]=tmp;
}
}
return arr;
}
3.2 堆排序
public static void sort(int []arr){
//1.构建大顶堆
for(int i=arr.length/2-1;i>=0;i--){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr,0,j);//重新对堆进行调整
}
}
//调整大顶堆
public static void adjustHeap(int []arr,int i,int length){
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
//交换元素
public static void swap(int []arr,int a ,int b){
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
4 归并排序
private static void mergeSort(int[] arr, int low, int high, int[] tmp) {
if (low>=high)
return;
int mid = (low+high)/2;
mergeSort(arr,low,mid,tmp); //对左边序列进行归并排序
mergeSort(arr,mid+1,high,tmp); //对右边序列进行归并排序
merge(arr,low,mid,high,tmp); //合并两个有序序列
}
private static void merge(int[] arr, int low, int mid, int high, int[] tmp) {
int i = 0;
int j = low, k = mid+1; //左边序列和右边序列起始索引
//比较左右两部分的元素,哪个小,把那个元素填入temp中
while(j <= mid && k <= high){
if(arr[j] < arr[k]){
tmp[i++] = arr[j++];
}else{
tmp[i++] = arr[k++];
}
}
while(j <= mid){ //若左边序列还有剩余,则将其全部拷贝进tmp[]中
tmp[i++] = arr[j++];
}
while(k <= high){
tmp[i++] = arr[k++];
}
for(int t=0; t<i; t++){
arr[low+t] = tmp[t]; //将tmp[]拷贝进arr[low++]中
}
}