数据结构与算法 10 排序算法 快速排序 基数排序 归并排序

排序算法

快速排序

对冒泡法的改进

时间复杂度:O(nlogn)

基本思想:通过一趟排序将要排序的数据分割成两个独立的部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行


package sort;

import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {1,34,2,4,56,23,7,-1,20};
        System.out.println(Arrays.toString(arr));
        quick(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    private static void quick(int[] arr,int left,int right) {
        int l = left;
        int r = right;
        int pivot = arr[(left+right)/2]; // middle value
        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;
            }
            // swrap
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            // 交换后 如果 arr[l] == pivot,则 r--,向前移一步
            if(arr[l]==pivot){
                r-=1;
            }
            // 交换后 如果 arr[r] == pivot,则 l++,向前移一步
            if(arr[r]==pivot){
                l+=1;
            }
        }
        // 如果 l==r 必须 l++ r-- 否则会出现栈溢出
        if(l==r){
            l+=1;
            r-=1;
        }
        // 向左递归
        if(left<r){
            quick(arr,left,r);
        }
        // 向右递归
        if(right>l){
            quick(arr,l,right);
        }
    }
}
时间测试:80000个随机数据
before sorting 2020-06-15 10:04:28
after sorting 2020-06-15 10:04:28

归并排序

merge-sort

时间复杂度:O(nlogn)

合并方法

public static void merge
1. 传入参数:
int[] arr 传入的需要排序的数组
int left 数组左边索引
int mid 数组中间索引
int right 数组右边索引
int[] temp 存放中间结果的数组

2. 定义变量
int i	左边有序序列的初始索引,初始值为 left
int j	右边有序序列的初始索引,初始值为 mid+1
int t 指向temp数组的当前索引,初始值为0

3. 先把左右两边(有序)的数组,按照规则填充到temp数组;直到左右两边的有序序列有一方处理完毕

4. while循环 i、j在各自范围内移动
如果左边有序序列当前元素小于等于右边有序序列的当前元素,把左边当前元素填充到temp中,后移 t 和 i;
反之,将右边有序序列的当前元素,填到temp中,后移 t 和 j

5. 用while循环把有剩余数据的一方的数据依次全部填充到temp中

6. 将temp数组元素拷贝到arr中,并不是每次都拷贝所有数据
定义变量 tempLeft指向原数组索引,t指向temp索引,进行填充

分+合

public static void mergeSort
1. 传入参数
int[] arr
int left
int right
int[] temp

2. 变量
int mid = (left+right)/2 中间索引

3. 在left<right条件下,向左递归进行分解:mergeSort(arr,left,mid,temp); 向右递归进行分解:mergeSort(arr,mid+1,right,temp); 每一步之后进行合并:merge(arr,left,mid,right,temp);

代码

package sort;

import java.util.Arrays;

public class MergeSortTest {
    public static void main(String[] args) {
        int[] arr = {1,34,2,4,56,23,7,-1,20};
        int[] temp = new int[arr.length]; // 额外的空间
        System.out.println(Arrays.toString(arr));
        mergeSort(arr,0,arr.length-1,temp);
        System.out.println(Arrays.toString(arr));
    }

    // divide+merge
    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);
        }
    }

    // merge
    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){ // keep going
            // 如果左边有序序列当前元素小于等于右边有序序列的当前元素
            // 把左边当前元素填到temp中
            // 后移 t,i
            if(arr[i]<=arr[j]){
                temp[t] = arr[i];
                t+=1;
                i+=1;
            }else{ // 将右边有序序列的当前元素,填充到temp数组
                temp[t] = arr[j];
                t+=1;
                j+=1;
            }
        }

        // 2
        // 把有剩余数据的一方的数据依次全部填充到temp中

        while(i<=mid){ // 左边有序序列有剩余元素,全部填到temp中
            temp[t] = arr[i];
            t+=1;
            i+=1;
        }

        while(j<=right){ // 右边有序序列有剩余元素,全部填到temp中
            temp[t] = arr[j];
            t+=1;
            j+=1;
        }

        // 3
        // 将 temp 数组的元素拷贝到 arr
        // 并不是每次都拷贝所有数据

        t = 0;
        int tempLeft = left;
        while(tempLeft<=right){
            // 第一次合并 tempLeft=0,right=1
            // 第二次合并 tempLeft=2,right=3
            // 第二次合并 tempLeft=0,right=3
            // 最后一次   tempLeft=0,right=7
            arr[tempLeft] = temp[t];
            t+=1;
            tempLeft+=1;
        }
    }
}
时间测试:8000000个随机数排序
before sorting 2020-06-15 11:03:38
after sorting 2020-06-15 11:03:40

基数排序(桶排序)

radix sort 属于“分配式排序” distribution sort,又称为“桶子法“ bucket sort

  1. 通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

  2. 基数排序是属于稳定性排序,效率较高

  3. 是桶排序的扩展

  4. 不支持负数


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


arr = [53,3,542,748,14,214]

总共有标号 0-9 十个桶

  • 第一轮排序
  1. 将每个元素的个位数取出,然后看这个数应该放在哪个对应的桶(一个一维数组)
  2. 按照桶的顺序(一维数组的下标依次取出数组放入原来的数组)
  3. 得到:arr[542,53,3,14,214,748]
  • 第二轮排序
  1. 将每个元素的十位数取出,然后看这个数应该放在哪个对应的桶(一个一维数组)
  2. 按照桶的顺序(一维数组的下标依次取出数组放入原来的数组)
  3. 得到:arr[3,14,214,542,748,53]
  • 第三轮排序
  1. 将每个元素的百位数取出,然后看这个数应该放在哪个对应的桶(一个一维数组)
  2. 按照桶的顺序(一维数组的下标依次取出数组放入原来的数组)
  3. 得到:arr[3,14,53,214,542,748]

找到最大位数

定义一个二维数组,表示10个桶,每个桶是一个一维数组
1. 二维数组包含10个一维数组
2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
3. 基数排序是使用空间换时间的经典算法

为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组来记录各个桶每次放入的数据个数

按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
遍历每一个桶,并将桶中的数据放入原数组
如果桶中有数据,我们才放入到原数组中
循环该桶,即第k个桶(第k个一维数组)
取出元素放入 arr

第一轮处理后,需要将每个 bucketElementCounts[k]=0!

轮数:最大位数
取模
清空每次的桶

代码

package sort;

import java.util.Arrays;

public class RadixSort2 {
    public static void main(String[] args) {
        int[] arr = {1,34,2,4,56,23,7,20};
        System.out.println(Arrays.toString(arr));
        radix(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void radix(int[] arr){
        // 1 先得到数组中最大的数的位数
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]>max){
                max = arr[i];
            }
        }
        // how many digits?
        int maxLength = (max+"").length();

        for (int j = 0,n=1; j < maxLength; j++,n*=10) {
            // 定义一个二维数组,表示10个桶,每个桶是一个一维数组
            // 1. 二维数组包含10个一维数组
            // 2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
            // 3. 基数排序是使用空间换时间的经典算法
            int[][] bucket = new int[10][arr.length];

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

            for (int i = 0; i < arr.length; i++) {
                // 取出每个元素的个位的值
                int 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个桶(第k个一维数组)
                    for(int l = 0; l < bucketElementCounts[k]; l++){
                        // 取出元素放入 arr
                        arr[index++] = bucket[k][l];
                    }
                }
            }
        }
    }
}
// 测试时间 8000000数据
before sorting 2020-06-15 12:37:47
after sorting 2020-06-15 12:37:48
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值