数据结构与算法——排序(2)

目录

五、快速排序

1.基本思想

2.代码实现

3.测试时间

六、归并排序

1.图示 

2.代码实现

 七、基数排序

1.基本思想

2.代码实现

八、排序算法时间复杂度比较


五、快速排序

  • 快速排序(Quicksort)是对冒泡排序法的一种改进。

1.基本思想

  1. 先从数列中取出一个数作为基准数。
  2. 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
  3. 再对左右区间重复第二步,直到各区间只有一个数。  

注:图中思想与代码思想一致,但基准值不同。 

2.代码实现:

public static void quickSort(int[] arr,int left,int right){
    int l = left;   //左下标
    int r = right;  //右下标
    int pivot = arr[(left + right)/2]; //中轴值
    int temp = 0; //用于交换的临时变量
    //while循环的目的是为了让比pivot值小的数放在左边,大的放在右边(从小到大排序)
    while (l < r){   //即选取左边的所有值
        //目的:在pivot的左边一直找大于或者等于pivot的值,才退出
        while (arr[l] < pivot){
            l += 1;
        }
        //目的:在pivot的右边一直找小于或者等于pivot的值,才退出
        while (arr[r] > pivot) {
            r -= 1;
        }
        //找到两边的两个数之后,交换
        temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
        //数据分配至两边两边同时寻找结束指标:即为左右两边开始产生交叉时
        if(l>=r){
            break;
        }
        //如果交换完后发现arr[l] = pivot值,此时让r--(前移)
        if(arr[l] == pivot){
            r--;
        }
        //如果交换完后发现arr[r] = pivot值,此时让l++(后移)
        if(arr[r] == pivot){
            l++;
        }
    }
    //如果 l == r(即两个都已经到达中间数据),必须 l++,r--,否则出现栈溢出(此时已经为了左递归和右递归铺垫了)
    if( l == r){
        l+=1;
        r-=1;
    }
    //向左递归
    if (left < r){
        quickSort(arr,left,r);
    }
    //向右递归
    if (right > l){
        quickSort(arr,l,right);
    }
    //左右排序结束
}

 3.测试时间

因为快速排序的速度太快,为了领运行时间更加明显,所以跟以往不同,这次arr中随机放入8000000个数据,得到结果:

六、归并排序

  • 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divid-and-conquer)策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的个答案“修补”再一起,即分而治之)。

1.图示 

2.代码实现

//合并方法
/**
 *
 * @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 l_l = left;  //i:左边有序序列的初始索引
    int r_l = mid + 1 ;  //中间值+1,即右边部分的最左边的索引
    int t = 0;    //指向temp数组的当前索引
    //1.先把左右两边(有序)按照规则填充到temp数组
    // 直到左右两边有一边已经处理完毕为止
    while (l_l <= mid && r_l <= right ){  //继续填充
        //如果说左边有序序列当先的元素小于右边有序序列的当前元素,则将左边的当前元素填充至temp中
        //然后 l和 t 均后移,(t则做好要填充的准备,l则进行比较)
        if(arr[l_l] <= arr[r_l]){
            temp[t] = arr[l_l];
            t++;
            l_l++;
        }else {  //反之,则将右边有序序列的当前元素填充至temp中
            temp[t] = arr[r_l];
            t++;
            r_l++;
        }
    }
    //2.把有剩余数据的一边的数据依次全部填充到temp中
    while (l_l <= mid){  //说明左边有序序列还有剩余的元素,则需要全部填充到temp中
        temp[t] = arr[l_l];
        t++;
        l_l++;
    }
    while (r_l <= right){
        temp[t] = arr[r_l];
        t++;
        r_l++;
    }
    //3.将temp数组重新填充至arr
    //注意:并不是每次上面的代码执行完毕之后拷贝的都是所有的元素
    t = 0;
    int tempLeft = left;
    while (tempLeft <= right){  //第一次合并时,tempL
        arr[tempLeft] = temp[t];
        t++;
        tempLeft++;
    }
 }
 //分 + 和的方法
 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);
     }
 }
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));
}

 七、基数排序

1) 基数排序(radixsort) 属于“分配式排序”(distributionsort) ,又称“桶子法”(bucket sort)或binsort, 顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

2)基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法

3)基数排序(Radix Sort)是桶排序的扩展

4)基数排序是1887年赫尔曼何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。

1.基本思想

        将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一一个有序序列。

arr = {321,1,10,60,577,743,127}

2.代码实现

package com.xx.Sort;

import java.util.Arrays;

public class RadixSort {
    public static void main(String[] args) {
        int[] arr = {321, 1, 10, 60, 577, 743, 127};
        radixSorx(arr);
    }

    public  static void radixSorx(int[] arr) {

        //第一轮排序(针对每个元素的各位进行排序处理)
        //定义一个二维数组,表示十个桶,每个桶就是一个一维数组
        //说明:为了防止在放入数据的时候,数据溢出,所以每个以为数组的桶的长度定为要排序的数组长度
        //基数排序是一种空间换时间的经典算法
        int[][] bucket = new int[10][arr.length];

        //为了记录10个桶中实际存放了多少个数据,我们定义一个以为数组来记录各个同每次放入的数据
        int[] bucketElementCounts = new int[10];

        //第一轮
        for (int i = 0; i < arr.length; i++) {
            //取出每个元素的个位
            int digitOfElement = arr[i] % 10;
            //放入每个桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
            bucketElementCounts[digitOfElement]++;
        }
        //按照桶的顺序(一维数组的下标依次取出,放入原来数组)
        int index = 0;
        for (int k = 0;k < bucketElementCounts.length;k++){
            //如果桶中有数据,我们才放入到原数组
            if(bucketElementCounts[k] != 0){
                //循环取出第k桶的数据
                for (int l = 0;l < bucketElementCounts[k];l++){
                    //取出元素放入到arr中
                    arr[index++] = bucket[k][l];
                }
            }
            //第一轮结束过后必须将每个bucketElementCounts置0
            bucketElementCounts[k] = 0;
        }
        System.out.println("第一轮对个位的排序处理:" + Arrays.toString(arr));

        //第二轮处理
        for (int i = 0; i < arr.length; i++) {
            //取出每个元素的十位
            int digitOfElement = (arr[i] / 10) % 10;   //得到十位
            //放入每个桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
            bucketElementCounts[digitOfElement]++;
        }
        index = 0;
        //按照桶的顺序(一维数组的下标依次取出,放入原来数组)
        for (int k = 0;k < bucketElementCounts.length;k++){
            //如果桶中有数据,我们才放入到原数组
            if(bucketElementCounts[k] != 0){
                //循环取出第k桶的数据
                for (int l = 0;l < bucketElementCounts[k];l++){
                    //取出元素放入到arr中
                    arr[index++] = bucket[k][l];
                }
            }
            bucketElementCounts[k] = 0;
        }
        System.out.println("第二轮对个位的排序处理:" + Arrays.toString(arr));

        //第三轮
        for (int i = 0; i < arr.length; i++) {
            //取出每个元素的百位
            int digitOfElement = (arr[i] / 100) % 10;   //得到十位
            //放入每个桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
            bucketElementCounts[digitOfElement]++;
        }
        index = 0;
        //按照桶的顺序(一维数组的下标依次取出,放入原来数组)
        for (int k = 0;k < bucketElementCounts.length;k++){
            //如果桶中有数据,我们才放入到原数组
            if(bucketElementCounts[k] != 0){
                //循环取出第k桶的数据
                for (int l = 0;l < bucketElementCounts[k];l++){
                    //取出元素放入到arr中
                    arr[index++] = bucket[k][l];
                }
            }
        }
        System.out.println("第二轮对个位的排序处理:" + Arrays.toString(arr));
    }
}

代码冗余,进行改进:

public static void radixSorx_(int[] arr) {
    int[][] bucket = new int[10][arr.length];
    int[] bucketElementCounts = new int[10];
    int digitOfElement = 0;
    //首先判断数组中最大的数是几位数,用以判断进行几轮放入桶中排序
    int max = arr[0];  //假设最大的数就是arr[0]
    for (int i = 0; i < arr.length; i++) {
        if(max <= arr[i]){
            max = arr[i];
        }
    }
    int maxLength = (max+" ").length() - 1;
    //第一轮
    for (int j = 0 , n = 1; j < maxLength; j++,n *= 10) {
        for (int i = 0; i < arr.length; i++) {
            //取出每个元素的个位
                digitOfElement = arr[i] / n % 10;
            //放入每个桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
            bucketElementCounts[digitOfElement]++;
        }
        //按照桶的顺序(一维数组的下标依次取出,放入原来数组)
        int index = 0;
        for (int k = 0; k < bucketElementCounts.length; k++) {
            //如果桶中有数据,我们才放入到原数组
            if (bucketElementCounts[k] != 0) {
                //循环取出第k桶的数据
                for (int l = 0; l < bucketElementCounts[k]; l++) {
                    //取出元素放入到arr中
                    arr[index++] = bucket[k][l];
                }
            }
            //第一轮结束过后必须将每个bucketElementCounts置0
            bucketElementCounts[k] = 0;
        }
        System.out.println("基数排序第 " + (j+1)  + "轮处理:" + Arrays.toString(arr));
    }
}

:数组中含有复数时,则不适用基数排序

八、排序算法时间复杂度比较

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值