排序算法及其代码实现(二)

本文介绍了三种常见的排序算法:快速排序、归并排序和基数排序。快速排序通过选取中轴值分割数组并递归排序;归并排序采用分治策略,将数组逐步合并成有序序列;基数排序则根据数位分配元素到桶中,通过多轮排序得到有序数组。重点讨论了每种算法的关键步骤和避免死循环的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5.快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两
部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排
序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。`
重点:
(1)在于需要选一个中轴值pivot,将数组分成左右两组,在分组的过程中,使用双指针分别从头和尾向中轴值pivot遍历,找到位置再一起交换,直到两个指针错位,结束遍历,完成分组。
(2)arr[l] == pivot 和 arr[r] == pivot时将左指针后移,右指针迁移,直到左右指针错位退出循环,防止一直原值交换死循环
(3)l=r,两个指针同时指向同一个数,也会原值交换,死循环,需要r–, l++,错开

public static void quickSort (int[]arr,int left,int right) {
        int l = left;//左下标,一般传入0,
        int r = right;//右下标,一般传入arr.length-1
        //pivot中轴值
        int pivot = arr[(left + right) / 2];
        int temp = 0;//临时变量,作为交换时用
        //while循环的目的是让比pivot值小的放到左边
        //让比pivot大的值放到右边
        while (l < r) {
            //第一步:双指针先定位,定好需要交换的位置
            //在pivot的左边一直找,找到大于等于pivot值时才退出
            while (arr[l] < pivot) {
                l += 1;
            }
            //在pivot的右边一直找,找到小于等于pivot值时才退出
            while (arr[r] > pivot) {
                r -= 1;
            }
            //如果l >= r说明pivot的左右两边的值,已经按照左边全小于等于pivot,右边全大于等于pivot排好了
            if (l >= r) {
                break;
            }
            //第二步:交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //交换完后判断,若arr[l] == pivot,则arr[r] == pivot
            // r和l会一直交换最外层的while死循环;所以要每交换一次,前移r, 后移l,直到l>=r,跳出循环
            if (arr[l] == pivot) {
                r -= 1;
            }
            if (arr[r] == pivot){
                l += 1;
            }
        }

        //第三步:分组后分别开始左右递归
        if (l==r){//l=r,说明两个都指向pivot,两个一直原值交换
            l+=1;
            r-=1;//将r和l错开,防止递归时栈溢出
        }

        //至此,已经r<l了,r,l 错开
        //向左递归,
        if (left < r){//r指针已经交换到pivot的前一个位置了
            quickSort(arr,left,r);
        }
        //向右递归
        if (right > l){//l指针已经交换到pivot的后一个位置了
            quickSort(arr,l,right);
        }
    }

6.归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)
策略。分,即将待排序数组分成两个数组(这个分并非真的有两个数组,只是通过指针划分出来的,类似“挡板”),逐渐递归分组直到一个元素为一组;治,将分得的组(两个有序数组)合并成一个有序数组,逐渐递归往上,最后合并成需要的有序数组,完成排序。
关键在于治,如何将两个有序数组合并成一个有序数组?
重点:
(1)需要一个temp数组存放要合并的两个有序序列数据;对两个有序序列分别从头开始遍历,并比较,将较小值依次放入temp

(2)将temp拷贝到arr[ ]数组

public static void main(String[] args) {
        int[] arr={8,4,5,7,1,3,6,2};
        int[] temp = new int[arr.length];//归并排序需要额外的空间
        mergeSort(arr,0,arr.length-1,temp);
        System.out.println("归并排序后"+ Arrays.toString(arr));
    }

    //分+合
    public static void mergeSort(int[] arr,int left,int right,int[] temp){
        if (left < right){
            int mid = (left+right)/2;//中间索引
            //向左递归分解
            mergeSort(arr,left,mid,temp);
            //向右递归分解
            mergeSort(arr,mid+1,right,temp);
            //合并
            merge(arr,left,mid,right,temp);
        }
    }

    /**
     *
     * @param arr 排序的原始数组
     * @param left 左边有序序列的初始索引
     * @param mid 中间索引
     * @param right 右边有序序列索引的末位索引
     * @param temp 中转的数组
     */
    public static void merge(int[] arr,int left,int mid,int right,int[] temp){
        int i=left;//初始化i,左边有序序列的初始索引
        int j=mid+1;//初始化j,右边有序序列的初始索引
        int t=0;//指向temp数组的当前索引

        //1.先把左右两边(有序)的数组按规则填充到temp数组
        //直到左右两边的有序序列,有一边处理完毕
        while (i<=mid && j<=right){
            if (arr[i] < arr[j]){
                temp[t] = arr[i];//将较小的arr[i]填到temp数组
                t+=1;
                i+=1;
            }else {//反之
                temp[t] = arr[j];//将较小的arr[j]填到temp数组
                t+=1;
                j+=1;
            }
        }

        //2.把有剩余数据的一边的数据依次全部填充到temp
        while (i <= mid){//左边有剩余
            temp[t]=arr[i];
            t+=1;
            i+=1;
        }
        while (j <= right){//右边有剩余
            temp[t]=arr[j];
            t+=1;
            j+=1;
        }

        //3.将temp数组拷贝到arr,并不是每一次都拷贝所有
        t=0;//指针从新指向数组首位
        int tempLeft=left;
        //第一次:tempLeft=0,right=1;
        //第二次:tempLeft=2,right=3
        //第三次:tempLeft=0,right=3
        //最后次:tempLeft=0,right=7
       System.out.println("tempLeft="+tempLeft+",right="+right);
        while (tempLeft <= right){
            arr[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
        }
    }

`7.基数排序

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort),它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
重点:
(1)获取待排序数组中的最大位数
(2)构造一个二维数组10*arr.length, 10位桶的个数,每一个桶分别代表0-9这10个数,每个数放到哪个桶需要通过计算:arr[j] /n % 10,操作个位时n=1,十位n=10,百位n=100,依次。。。。。。
(3)需要bucketElementCounts数组对每个桶中的元素个数进行统计,并在每一轮排序将桶中元素取出放回到原数组后清0,

public static void radixSort(int[] arr){
        //得到数组arr[]中最大的数的位数
        int max = arr[0];//假设第一个数就是最大数
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr [i];
            }
        }
        //得到最大是几位数
        int maxLength = (max + "").length();

        //二维数组包含10个一维数组,每个一维数组就是一个桶
        //为防止栈溢出(最坏情况个位数相同),一维数组设为arr.length
        //基数排序是空间换时间的经典算法
        int[][] bucket = new int [10][arr.length];

        //为记录每个桶中每轮实际存放数据个数,再定义一个一维数组
        //bucketElementCounts[0],记录桶bucket[0]放入的数据,以此类推
        int[] bucketElementCounts = new int[10];

        for (int i = 0, n=1; i < maxLength; i++,n *=10) {
            //第i轮,针对每个元素对应的位(个n=1,十n=10,百n=100,千n=1000。。。)
            for (int j = 0; j <arr.length ; j++) {
                //取出每个元素的对应位的值
                int digitOfElement = arr[j] /n % 10;
                //当前数放入对应桶中
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement] ++;
            }
            //按照这个桶的顺序(一维数组的下标一次取出数据,放入原来的数组)
            int index = 0;//原数组arr【】的索引
            //遍历每一个桶,并将桶中数据放入到原数组。
            for (int k = 0; k <bucketElementCounts.length ; k++) {
                //如果桶中有数据,才放入到原数组
                if (bucketElementCounts[k] !=0){
                    //遍历该桶,将该第k个桶里的所有数据(l个数),依次放入原数组
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        arr[index ++] = bucket[k][l];
                    }
                }
                //第i+1轮处理后,需要把每一个bucketElementCounts[k]=0
                bucketElementCounts[k]=0;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值