排序算法及复杂度分析

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
冒泡排序O(n2)O(n2)O(n)O(1)稳定
选择排序O(n2)O(n2)O(n2)O(1)不稳定
插入排序O(n2)O(n2)O(n)O(1)稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
快速排序O(nlogn)O(n2)O(nlogn)O(nlogn)不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定
希尔排序O(n2)O(n2)O(n)O(1)不稳定
1.冒泡排序
过程
  • 一开始交换的区间为[1,n-1],然后时第一个数和第二个数比较,哪个大就互换到后面。然后第二个数和第三个数比较,哪个大就互换到后面。依次比较并交换,最后最大的数被放到最右边。
  • 然后把范围区间缩小为[1,n-2],重复以上过程,直到范围缩小到1,完成排序。
    在这里插入图片描述
复杂度
  • 最好 O(n)
    • 最好的情况下,就是正序,所以只要比较一次就行了,复杂度O(n)
  • 最坏 O(n2)
    • 最坏的情况下,就是逆序,要比较n2次才行,复杂度O(n2)
  • 平均O(n2)

总结:时间复杂度O(n2),空间复杂度O(1),当待排序列有序时,效果比较好。

稳定性
  • 稳定
    比较时,相等的两数不交换位置。
示例代码
import java.util.*;
public class BubbleSort {
    public int[] bubbleSort(int[] A, int n) {
        // write code here
        int k=n-1;
        //int pos=n-1;
        int pos=0;
        for(int i=n-2;i>=0;i--){
            for(int j=0;j<k;j++){
                if(A[j]>A[j+1]){
                    swap(A,j,j+1);
                    pos=j;
                }
            }
            k=pos;
        }
        return A;
    }
    public void swap(int[] arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
}
2.选择排序
过程
  • 在范围[0,n-1]中选择最小的数,和第一个数交换,在范围[1,n-1]中选择最小的数,和第二个数交换。这样依次直到最后范围只剩一个数时,整个数组变成有序了。

在这里插入图片描述

复杂度
  • O(n2),和序列的初始状态无关

总结:时间复杂度O(n2),无论最好还是最坏。空间复杂度是O(1)

稳定性
  • 不稳定
    因为可能需要和范围内最小值交换位置,相等的两个数初始时的相对位置到最后可能改变。
示例代码
import java.util.*;
public class SelectionSort {
    public int[] selectionSort(int[] A, int n) {
        // write code here
        int minIndex=0;;
        for(int i=0;i<n;i++){
            minIndex=i;
            for(int j=i+1;j<n;j++){
                if(A[j]<A[minIndex]){
                    minIndex=j;
                }
            }
            swap(A,minIndex,i);
        }
        return A;
    }
    public void swap(int[] arr,int a,int b){
        int t=arr[a];
        arr[a]=arr[b];
        arr[b]=t;
    }
}
3.插入排序
过程
  • 位置1上的数和位置0上的数比较,如果位置1上的数小,则和位置0上的数交换。
  • 位置2上的数a依次与位置1、0的数比较,如果1上的数比a大,则往后移一位,a继续依次和前面的数比较,直到找到前面的数小于等于a,把a放入找到的位置。
    在这里插入图片描述
复杂度
  • 最好O(n)

  • 最坏O(n2)

  • 平均O(n2)

  • 直接插入排序法在最好情况下(待排序列已按关键码有序),每趟排序只需作1次比较而不需要移动元素。所以n个元素比较次数为n-1,移动次数0。

  • 最差的情况下(逆序),其中第i个元素必须和前面的元素进行比较i次,移动个数i+1

总结:时间复杂度O(n2),排序过程中只要一个辅助空间,所以空间复杂度O(1)

稳定性
  • 稳定
    相等的两个数相对位置不变。
示例代码
import java.util.*;

public class InsertionSort {
    public int[] insertionSort(int[] A, int n) {
        // write code here
        for(int i=1;i<n;i++){
            int j=i-1;
            int temp=A[i];
            for(;j>=0;j--){
                if(temp<A[j]){
                    A[j+1]=A[j];
                }else{
                    break;
                }
            }
            A[j+1]=temp;
        }
        return A;
    }
    public void swap(int[] arr,int a,int b){
        int t=arr[a];
        arr[a]=arr[b];
        arr[b]=t;
    }
}
4.归并排序
过程
  • 首先让数组中的每一个数单独成为长度为1的区间,然后两两一组有序合并,得到长度为2的有序区间,依次进行,直到合成整个区间。
    在这里插入图片描述
复杂度
  • 最好O(nlogn)
  • 最坏O(nlogn)
  • 平均O(nlogn)
    一趟归并需要将待排序列中的所有记录扫描一遍,因此耗费时间为O(n),而由完全二叉树的深度可知,整个归并排序需要进行[log2n],因此,总的时间复杂度为O(nlogn),而且这是归并排序算法中平均的时间性能

空间复杂度:由于归并过程中需要与原始记录序列同样数量级的 存储空间去存放归并结果及递归深度为log2N的栈空间,因此空间复杂度为O(n+logN) 也就是说,归并排序是一种比较占内存,但却效率高且稳定的算法。

稳定性
  • 稳定
示例代码
import java.util.*;

public class MergeSort {
    public int[] mergeSort(int[] A, int n) {
        // write code here
        if(A==null||A.length<2){
            return null;
        }
        __mergeSort(A,0,n-1);
        return A;
    }
    public void __mergeSort(int arr[],int l,int r){
        if(l==r){
            return;
        }
        int mid=(l+r)/2;
        __mergeSort(arr,l,mid);
        __mergeSort(arr,mid+1,r);
        __merge(arr,l,mid,r);
    }
    public void __merge(int[] arr,int left,int mid,int right){
        int[] aux=new int[right-left+1];
        int l=left;
        int r=mid+1;
        int i=0;
        while (l<=mid&&r<=right){
            if(arr[l]<=arr[r]){
                aux[i]=arr[l];
                l++;
            }else {
                aux[i]=arr[r];
                r++;
            }
            i++;
        }
        while (l<=mid){
            aux[i]=arr[l];
            l++;
            i++;
        }
        while (r<=right){
            aux[i]=arr[r];
            r++;
            i++;
        }
        for( i=0;i<aux.length;i++){
            arr[i+left]=aux[i];
        }
    }
}
5.快速排序
过程
  • 在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。
复杂度
  • 最好O(nlogn)
  • 最坏O(n2)
  • 平均O(nlogn)
稳定性
  • 不稳定
示例代码
import java.util.*;

public class QuickSort {
    public int[] quickSort(int[] A, int n) {
        // write code here
        if(A!=null||A.length>1){
            __quickSort(A,0,n-1);
        }
        return A;
    }
    public  void __quickSort(int[] arr,int left,int right){
        if(left>=right){
            return;
        }
        int random= (int) (left+Math.random()*(right-left+1));
        swap(arr,random,right);
        int mid=partition(arr,left,right);
        __quickSort(arr,left,mid-1);
        __quickSort(arr,mid+1,right);
    }
    //划分过程
    public  int partition(int[] arr,int left,int right) {
        int i=left;
        int pivot=left-1;
        while (i<=right){
            if(arr[i]<=arr[right]){
                swap(arr,i,++pivot);
            }
            i++;
        }
        //i=right时,swag(arr,pivot,right)把基准数arr[right]换到左边小于等于堆右第一个
        return pivot;
    }
    public void swap(int[] arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
}
6.堆排序
过程
  • 首先将数组元素建成大小为n的大顶堆,堆顶(数组第一个元素)是所有元素中的最大值,将堆顶元素和数组最后一个元素进行交换,再将除了最后一个数的n-1个元素建立成大顶堆,再将最大元素和数组倒数第二个元素进行交换,重复直至堆大小减为1。
  • 大顶堆
    大顶堆是具有以下性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值。
    即,根节点是堆中最大的值,按照层序遍历给节点从1开始编号,则节点之间满足如下关系:
    这里写图片描述
    在这里插入图片描述
    (1<=i<=n/2)在这里插入图片描述
复杂度
  • 最好O(nlogn)
  • 最坏O(nlogn)
  • 平均O(nlogn)
稳定性
  • 不稳定
示例代码
void heapSort(int array[], int n)
{
    int i;
    for (i=n/2;i>0;i--)
    {
        HeapAdjust(array,i,n);//从下向上,从右向左调整
    }
    for( i=n;i>1;i--)
    {
        swap(array, 1, i);
        HeapAdjust(array, 1, i-1);//从上到下,从左向右调整
    }
}
void HeapAdjust(int array[], int s, int n )
{
    int i,temp;
    temp = array[s];
    for(i=2*s;i<=n;i*=2)
    {
        if(i<n&&array[i]<array[i+1])
        {
            i++;
        }
        if(temp>=array[i])
        {
            break;
        }
        array[s]=array[i];
        s=i;
    }
    array[s]=temp;
}
void swap(int array[], int i, int j)
{
    int temp;

    temp=array[i];
    array[i]=array[j];
    array[j]=temp;
}
7.希尔排序
过程
  • 是插入排序改良的算法,希尔排序步长从大到小调整,第一次循环后面元素逐个和前面元素按间隔步长进行比较并交换,直至步长为1,步长选择是关键。
    在这里插入图片描述
复杂度
  • 最好O(n)
  • 最坏O(n2)
  • 平均O(n2)
稳定性
  • 稳定
示例代码
import java.util.*;

public class ShellSort {
    public int[] shellSort(int[] A, int n) {
        // write code here
        int gap=n/2;
        for(;gap>0;gap/=2){
            for(int i=gap;i<n;i++){
                for(int j=i;j-gap>=0&&A[j]<A[j-1];j-=gap){
                    swap(A,j-1,j);
                }
            }
        }
        return A;
    }
    public void swap(int[]a,int i,int j){
        int temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }
}

参考:https://blog.csdn.net/yushiyi6453/article/details/76407640

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值