八大排序算法的java实现

冒泡排序

  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个,小的数往上冒
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
   public static void bubbleSort(int[] numbers)
   {
       int temp = 0;
       int size = numbers.length;
       for(int i = 0 ; i < size-1; i ++)
       {
           //一趟比较之后,最大的元素就到了最右边(最下面)
           for(int j = 0 ;j < size-1-i ; j++)
           {
               //小数往上冒,大数下沉
               if(numbers[j] > numbers[j+1])
               {
                   temp = numbers[j];
                   numbers[j] = numbers[j+1];
                   numbers[j+1] = temp;
               }
           }
       }
   }

快速排序

在这里插入图片描述

    public static void quickSort(int[] arr, int low, int high) {
        int i, j, temp;
        if (low > high) {
            return;
        }

        i = low;//左边哨兵的索引
        j = high;//右边哨兵的索引
        temp = arr[low];//以最左边为基准位

        while (i < j) {
            //先看右边,依次往左递减
            //先从右往左找一个小于 基准位的数
            //当右边的哨兵位置所在的数>基准位的数时
            //继续从右往左找(同时 j 索引-1)
            //找到后会跳出while循环
            while (temp <= arr[j] && i < j) {
                j--;
            }

            //再看左边,依次往右递增,步骤和上面类似
            while (temp >= arr[i] && i < j) {
                i++;
            }

            //如果满足条件则交换
            if (i < j) {
                // 左右哨兵 交换数据(互相持有对方的数据)
                arr[i] = arr[i]^arr[j];
                arr[j] = arr[i]^arr[j];
                arr[i] = arr[i]^arr[j];
            }
        }

        //这时 跳出了 “while (i<j) {}” 循环,说明 i=j 左右在同一位置
        //最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[i];//或 arr[low] = arr[j];
        arr[i] = temp;//或 arr[j] = temp;

        //递归调用左半数组
        quickSort(arr, low, j - 1);
        //递归调用右半数组
        quickSort(arr, j + 1, high);
    }

直接选择排序

  • 在要排序的一组数中,选出最小的一个数与第一个位置的数交换;
  • 然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环直到只剩两个数做最后一次排序。
    public static void selectSort(int[] numbers)
    {
        for(int i = 0 ; i < numbers.length -1 ; i++)
        {
            int k = i;
            //选择出应该在第i个位置的数
            for(int j = numbers.length -1 ; j > i ; j--)
            {
                if(numbers[j] < numbers[k]) k = j;
            }
            //交换两个数
            numbers[i] = numbers[i]^numbers[k];
            numbers[k] = numbers[i]^numbers[k];
            numbers[i] = numbers[i]^numbers[k];
        }
    }

直接插入排序

  • 选出最左边两个元素,排序,得到一个排序好的子集
  • 选出左边的第三个元素,插入到排序好的子集当中(从右到左遍历,找到合适的插入位置)
  • 循环执行第二部,直到最后一个元素。
    public static void insertSort(int[] numbers)
    {
        int temp,j;

        for(int i = 0 ; i < numbers.length ; i++)
        {
            temp = numbers[i];
            //假如temp比前面的值小,则将前面的值后移
            for(j = i ; j > 0 && temp < numbers[j-1] ; j --)
            {
                numbers[j] = numbers[j-1];
            }
            numbers[j] = temp;
        }
    }

希尔排序(直接插入改良版)

插入排序当数组基本有序时,比较高效。但是对于较大规模且无序的数据,插入排序效率就低了。可以采用希尔排序。
在这里插入图片描述
可以看出,他是按下标相隔距离为4分的组,也就是说把下标相差4的分到一组,比如这个例子中a[0]与a[4]是一组、a[1]与a[5]是一组…,这里的差值(距离)被称为增量。
每个分组进行插入排序后,各个分组就变成了有序的了(整体不一定有序)。
在这里插入图片描述
然后缩小增量为上个增量的一半:2,继续划分分组,此时,每个分组元素个数多了,但是,数组变的部分有序了,插入排序效率同样比高。
在这里插入图片描述
同理对每个分组进行排序(插入排序),使其每个分组各自有序。
在这里插入图片描述
最后设置增量为上一个增量的一半:1,则整个数组被分为一组,此时,整个数组已经接近有序了,插入排序效率高。
在这里插入图片描述
对以上数组再进行插入排序后,希尔排序就全部完成了。
有一点需要特别补充,对各个组进行插入排序的时候,并不是先对一个组进行排序完,再对另一个组排序。而是每个组并发执行的,每次插入一个元素进行排序。
在这里插入图片描述

public static void shellSort(int [] arr){
    int N = arr.length;
    //进行分组,最开始的时候,增量为数组的一半,即分成N/2组
    for(int gap=N/2;gap>0;gap/=2){
        /**
         * 对每组进行插入排序
         * 对各个组进行插入排序的时候,
         * 并不是先对一个组进行排序完,再对另一个组排序
         * 而是每个组并发,每次插入一个元素进行排序
         */
        for(int i = gap; i<N; i++){
            //将arr[i]插入到分组的正确位置上
            int insert = arr[i];
            int idx;
            for(idx = i-gap; idx >=0 && insert<arr[idx]; idx-=gap){
                arr[idx+gap] = arr[idx];
            }
            arr[idx+gap] = insert;
        }
    }
}

归并排序

归并(Merge)排序法是将两个有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
合并方法:

设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。
1、j=m+1;k=i;i=i; //置两个子表的起始下标及辅助数组的起始下标
2、若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束
3、//选取r[i]和r[j]较小的存入辅助数组rf
        如果r[i]<r[j],rf[k]=r[i]; i++; k++; 转⑵
        否则,rf[k]=r[j]; j++; k++; 转⑵
4、//将尚未处理完的子表中元素存入rf
        如果i<=m,将r[i…m]存入rf[k…n] //前一子表非空
        如果j<=n ,  将r[j…n] 存入rf[k…n] //后一子表非空
5、合并结束。

代码实现

    public static int[] mergeSort(int[] nums, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            mergeSort(nums, low, mid);
            // 右边
            mergeSort(nums, mid + 1, high);
            // 左右归并
            {
                int[] temp = new int[high - low + 1];
                int i = low;// 左指针
                int j = mid + 1;// 右指针
                int k = 0;

                // 把较小的数先移到新数组中
                while (i <= mid && j <= high) {
                    if (nums[i] < nums[j]) {
                        temp[k++] = nums[i++];
                    } else {
                        temp[k++] = nums[j++];
                    }
                }

                // 把左边剩余的数移入数组
                while (i <= mid) {
                    temp[k++] = nums[i++];
                }

                // 把右边边剩余的数移入数组
                while (j <= high) {
                    temp[k++] = nums[j++];
                }

                // 把新数组中的数覆盖nums数组
                for (int k2 = 0; k2 < temp.length; k2++) {
                    nums[k2 + low] = temp[k2];
                }
            }
        }
        return nums;
    }

堆排序(直接选择改良版)

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
详细介绍可以参考下面这篇文章
https://www.cnblogs.com/chengxiao/p/6129630.html
堆排序是一种选择排序,概括如下:

  • 构建初始堆(大顶堆或小顶堆)
  • 交换堆顶元素和末尾元素(选择出了最大或最小元素
  • 剔除末尾元素,回到第一步重建堆的操作,如此循环直到结束。
    其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。
    代码实现:
    public static void hepSort(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--){
            //将堆顶元素与末尾元素进行交换
            int temp=arr[0];
            arr[0] = arr[j];
            arr[j] = temp;
            //重新对堆进行调整
            adjustHeap(arr,0,j);
        }

    }

    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
     * @param arr
     * @param i
     * @param length
     */
    private 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值放到最终的位置
    }

基数排序

基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
1.先按照个位数的数值,遍历数组,把它们分配至0到9的桶中。
2.将桶中的值重新串联起来
3.按照十位数的数值,遍历2串联起来的数组,把它们分配到0到9的桶中。
4.将桶中的值重新串联起来
5.如果待排序数组中有百位,千位数值,则重复3,4步操作,直至最高位排序完。
https://www.runoob.com/w3cnote/radix-sort.html

总结

在这里插入图片描述
希尔排序是选择排序的改良版,堆排序是快速排序的改良版。
这四种排序都属于不稳定排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值