常用的排序算法及java实现

常用的排序算法有:

               冒泡排序选择排序插入排序归并排序堆排序快速排序等。

排序算法稳定性

稳定性的简单形式化定义为:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。通俗地讲就是保证排序前后两个相等的数的相对顺序不变。

对于不稳定的排序算法,只要举出一个实例,即可说明它的不稳定性;而对于稳定的排序算法,必须对算法进行分析从而得到稳定的特性。需要注意的是,排序算法是否为稳定的是由具体算法决定的,不稳定的算法在某种条件下可以变为稳定的算法,而稳定的算法在某种条件下也可以变为不稳定的算法。

1.冒泡排序

    如果将记录交换的条件改成A[i] >= A[i + 1],则两个相等的记录就会交换位置,从而变成不稳定的排序算法。

时间复杂度:平均为 O(n2),最好情况 即已经排序的数组,O(n)

    private static void sort(int[] arr) {
        for (int  i = 0;i<arr.length-1;i++)
            for (int j=0;j<arr.length-1-i;j++){ //从前往后 从小到大
                if (arr[j]>arr[j+1])
                    swap(arr,j,j+1);
            }

    }

    private static void sort2(int[] arr) {
        for (int  i = 0;i<arr.length-1;i++)
            for (int j=0;j<arr.length-1-i;j++){ //从前往后 从大到小
                if (arr[j]<arr[j+1])
                    swap(arr,j,j+1);
            }
    }

冒泡排序的优化:

用一个flag来表示排序已经完成,没有发生交换,那么就跳出循环。

    //冒泡排序优化
    private static void sortBetter(int[] arr) {
        boolean flag = false;
        for (int  i = 0;i<arr.length-1;i++)
        {
            flag = false;
            for (int j=0;j<arr.length-1-i;j++)
            { //从前往后 从小到大
                    if (arr[j]>arr[j+1]) {
                    swap(arr,j,j+1);
                    flag = true;
                    }
            }
                if (flag = false)// 整个循环 都没有发生交换 说明 数组已经有序 跳出循环,无需进行排序
                    break;
        }
    }

2.简单选择排序

它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

时间复杂度:平均:O(n2) 最好最差:O(n2) 

选择排序是不稳定的排序算法,不稳定发生在最小元素与A[i]交换的时刻。

  比如序列:{ 5, 8, 52, 9 },一次选择的最小元素是2,然后把2和第一个5进行交换,从而改变了两个元素5的相对次序。

//简单选择排序,每次选择未排序序列中最小的放在 有序数列的末尾
    //与冒泡排序的区别 冒泡排序每次遇到后面比前面小的数都要交换,而选择排序只交换 当前序列末尾的数和 未排序中最小的数
    private static void sort(int[] arr) {
        int min = 0;
        for (int i= 0;i<arr.length;i++)
        {
            min = i;
            for (int j=i+1;j<arr.length;j++){
                if (arr[j]<arr[min])
                    min = j;// 只是将脚标给他
            }
            swap(arr,min,i); //交换 当前序列未排序最小的数和 已排序最后末尾的数
        }
    }

3.插入排序

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5

插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,比如量级小于千,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为8个或以下)。

时间复杂度:O(n2) 最好情况:已排序 O(n) 最差:O(n)

稳定排序

    //插入排序
    // 首先认为第一个数时有序的,从后面的一个数开始往前插,如果前面一个数,大于这个tmp数,
    //那么前面这个数后移一个,一直到前面的数小于tmp,那么tmp就在这个j的位置
    private static void sort(int[] arr) {
        //注意这个J
        int j;
        for (int i =1;i<arr.length;i++)
        {
            int tmp = arr[i];
            for ( j =i;j>0 && arr[j-1]>tmp;j--){
                arr[j]=arr[j-1];
            }
            arr[j]=tmp;
        }
    }

4.希尔排序

希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

    //希尔排序
    //插入排序增强版
    //常规插入排序只是一次比较间隔位大小,建个永远为一。
    //希尔排序使用大间隔持续缩小,对间隔内元素分组,这样相邻较远的元素优先排序,最后变成插入排序
    private static void sort(int[] arr) {
        //间隔去 leng/2
        for (int len = arr.length/2;len>0;len/=2)
        {
            for (int i = len;i<arr.length;i++){
                int j = i;
                while (j-len>=0 && arr[j]<arr[j-len]){
                    swap(arr,j,j-len);
                    j-=len;
                }
            }
        }
    }

     在最优的情况下,时间复杂度为:O(n ^ (1.3) )   (元素已经排序好顺序)

    在最差的情况下,时间复杂度为:O(n ^ 2);

 

5.快速排序

快排原理:

        在要排的数(比如数组A)中选择一个中心值key(比如A[0]),通过一趟排序将数组A分成两部分,其中以key为中心,key右边都比key大,key左边的都key小,然后对这两部分分别重复这个过程,直到整个有序。

        整个快排的过程就简化为了一趟排序的过程,然后递归调用就行了。

        一趟排序的方法:

1,定义i=0,j=A.lenght-1,i为第一个数的下标,j为最后一个数下标

2,从数组的最后一个数Aj从右往左找,找到第一小于key的数,记为Aj;

3,从数组的第一个数Ai 从左往右找,找到第一个大于key的数,记为Ai;

4,交换Ai 和Aj 

5,重复这个过程,直到 i=j

6,调整key的位置,把A[i] 和key交换

时间复杂度:O(nlogn) 

    private static void Quicksort(int[] arr, int low, int high) {
        //递归 结束
        if (low>high)
            return;
        int i = low;
        int j =high;
        int key = arr[low];//基准
        while (i<j){
            while (i<j && arr[j]>key)
                j--;
            while (i<j && arr[i]<key)
                i++;
            if (i<j)
                swap(arr,i,j);
        }//完成一次排序
        swap(arr,i,low);//i 此时和就重合,交换key和i的位置 完成一次
        //排左边
        Quicksort(arr,low,i-1);
        Quicksort(arr,i+1,high);
    }

 

6.堆排序

利用最大堆或者最小堆完成排序。先初始化一个最大堆(从小到大排),然后取出堆顶元素,放在末尾,调整该堆使其符合堆的定义。不断重复上述,完成堆排序。

    //堆排序
    private static void sort(int[] arr) {
        // 初始化堆 从 len/2处开始往上初始化
        for (int len = arr.length/2-1;len>=0;len--){
            adjust(arr,len,arr.length);
        }
        //调整堆 排序
        for (int i = arr.length-1; i>=0;i--){
            //交换首尾的元素
            swap(arr,0,i);
            //调整堆
            adjust(arr,0,i);
        }
    }
    private static  void  adjust(int[] arr,int left,int len) {
        int tmp = arr[left];
        for (int k = 2 * left + 1; k < len; k = 2 * k + 1) {
            if (k + 1 < len && arr[k] < arr[k + 1])
                k++;
            if (tmp < arr[k]) {
                arr[left] = arr[k];
                left = k;
            } else break;
        }//都调整完
        arr[left] = tmp;
    }
    private static void swap(int[] arr, int j, int i) {
        int tmp = arr[j];
        arr[j] = arr[i];
        arr[i] = tmp;
    }

 

7.归并排序

分治的思想,先将数组分为(二分法)若干份,然后直到分成一组只有2个,将这两个进行排序,创建临时数组,比较然后放入临时数组,然后递归完成此操作。

 private static void sort(int[] arr) {
        int [] tmp = new int[arr.length];
        bingSort(arr,0,arr.length-1,tmp);
    }

    private static void bingSort(int[] arr, int left, int right,int  [] tmp) {
        if (left<right){
            int mid = (left+right)/2;
            bingSort(arr,left,mid,tmp);//左子序列 分割 left-mid 这一部分
            bingSort(arr,mid+1,right,tmp);//右子序列
            merge(arr,left,mid,right,tmp);//对左右子序列进行归并排序
        }
    }

    private static void merge(int[] arr, int left, int mid, int right, int[] tmp) {
        int i = left;
        int j = mid+1;
        int index = 0;
        while (i<=mid &&j<=right )
        {
            if (arr[i]<arr[j])
            tmp[index++] = arr[i++];
            else
                tmp[index++] = arr[j++];
        }
        while (i<=mid){
            tmp[index++] = arr[i++];
        }
        while (j<=right)
            tmp[index++] = arr[j++];
         index = 0;
        while (left<=right){
            arr[left++] = tmp[index++];
        }
    }

猜你喜欢

转载自blog.csdn.net/weixin_38035852/article/details/81412838