排序算法 计数排序(普通计数排序、桶排序)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_36511401/article/details/102647839

一、计数排序

1、介绍。

        计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)。

        计数排序是稳定排序,需要额外内存,空间复杂度O(k)。时间复杂度,最佳情况:O(n+k)  最差情况:O(n+k)  平均情况:O(n+k)。

2、步骤。

    (1)输入的线性表的元素属于有限偏序集S;
    (2)设输入的线性表的长度为n,|S|=k(表示集合S中元素的总数目为k),则k=O(n)。
        在这两个条件下,计数排序的复杂性为O(n)。计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些元素放在输出序列的同一个位置上,因此,上述方案还要作适当的修改。

3、代码。

public static void main(String[] args) {
        System.out.println("------开始------");
        //生成生成两份一模一样的随机数组,其中一组用系统自带的方法进行排序,到时候进行验证。
        final int number = 100000;
        int[] sortArray = new int[number];
        int[] sortArrayCopy = new int[number];
        for (int i = 0; i < sortArray.length; i++) {
            sortArray[i] = (int) (Math.random() * number);
        }
        System.arraycopy(sortArray, 0, sortArrayCopy, 0, number);//数组复制
        Arrays.sort(sortArrayCopy);

        //开始排序
        long startTime = System.currentTimeMillis();
        countSort(sortArray);//计数排序
        System.out.println("花费时间:" + (System.currentTimeMillis() - startTime));

        //跟系统排序之后数组进行比较,查看是否排序成功。
        if (Arrays.equals(sortArray, sortArrayCopy)) {
            System.out.println("排序成功");
        } else {
            System.out.println("排序失败");
        }
        System.out.println("------结束------");
    }
//计数排序 最佳情况:T(n) = O(n+k)  最差情况:T(n) = O(n+k)  平均情况:T(n) = O(n+k)
private static void countSort(int[] array) {
    //找出array的最大值和最小值
    int max = array[0], min = array[0];
    for (int i = 1; i < array.length; i++) {
        if (array[i] > max) {
            max = array[i];
        } else if (array[i] < min) {
            min = array[i];
        }
    }
    //开始计数
    int[] countArray = new int[max - min + 1];
    for (int i = 0; i < array.length; i++) {
        countArray[array[i]]++;
    }
    //排序
    int index = 0;
    for (int i = 0; i < countArray.length; i++) {
        if (countArray[i] != 0) {
            for (int j = 0; j < countArray[i]; j++) {
                array[index++] = i;
            }
        }
    }
}

4、结果。

二、桶排序

1、介绍。

        桶排序 (Bucket sort)或所谓的箱排序,是一个计数排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。假定:输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)。

        桶排序是稳定排序,需要额外内存,空间复杂度O(n+k)。时间复杂度,最佳情况:O(n+k)  最差情况:O(n^2)  平均情况:O(n+k)。

2、步骤。

    (1)人为设置一个BucketSize,作为每个桶所能放置多少个不同数值(例如当BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3);
    (2)遍历输入数据,并且把数据一个一个放到对应的桶里去;
    (3)对每个不是空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序;
    (4)从不是空的桶里把排好序的数据拼接起来。 

3、代码。

public static void main(String[] args) {
        System.out.println("------开始------");
        //生成生成两份一模一样的随机数组,其中一组用系统自带的方法进行排序,到时候进行验证。
        final int number = 100000;
        int[] sortArray = new int[number];
        int[] sortArrayCopy = new int[number];
        for (int i = 0; i < sortArray.length; i++) {
            sortArray[i] = (int) (Math.random() * number);
        }
        System.arraycopy(sortArray, 0, sortArrayCopy, 0, number);//数组复制
        Arrays.sort(sortArrayCopy);

        //开始排序
        long startTime = System.currentTimeMillis();
        bucketSort(sortArray);//桶排序
        System.out.println("花费时间:" + (System.currentTimeMillis() - startTime));

        //跟系统排序之后数组进行比较,查看是否排序成功。
        if (Arrays.equals(sortArray, sortArrayCopy)) {
            System.out.println("排序成功");
        } else {
            System.out.println("排序失败");
        }
        System.out.println("------结束------");
    }
//桶排序 最佳情况:T(n) = O(n+k)  最差情况:T(n) = O(n2)  平均情况:T(n) = O(n+k)
private static void bucketSort(int[] array) {
    //找出array的最大值和最小值
    int max = array[0], min = array[0];
    for (int i = 1; i < array.length; i++) {
        if (array[i] > max) {
            max = array[i];
        } else if (array[i] < min) {
            min = array[i];
        }
    }
    int length = max - min + 1;//区间
    int bucketCount = length / 10;//桶的个数,如果个数为length个,就是计数排序了
    int range = (int) Math.ceil(length * 1.0 / bucketCount);//记得要Math.ceil
    int[][] bucketArray = new int[bucketCount][];
    for (int i = 0; i < array.length; i++) {
        int bucketIndex = (int) Math.floor((array[i] - min) / range);//判断array[i]是属于第几个区间的
        bucketArray[bucketIndex] = addArray(bucketArray[bucketIndex], array[i]);
    }
    //排序(记得判断bucketArray[i] != null)
    int index = 0;
    for (int i = 0; i < bucketArray.length; i++) {
        if (bucketArray[i] != null && bucketArray[i].length != 0) {
            for (int j = 0; j < bucketArray[i].length; j++) {
                array[index++] = bucketArray[i][j];
            }
        }
    }
}

private static int[] addArray(int[] array, int item) {
    //(记得判断bucketArray[i] != null)
    if (array == null) {
        return new int[]{item};
    }
    int[] newArray = new int[array.length + 1];
    boolean flag = false;//Item是否已经插入
    for (int i = array.length; i >= 0; i--) {
        if (flag) {//item已经插入,剩下的一个个赋值过去
            newArray[i] = array[i];
        } else if (i == 0) {//item比array的都小
            newArray[0] = item;
            break;
        } else if (item >= array[i - 1]) {//找到比item小的了,插入item
            newArray[i] = item;
            flag = true;
        } else {
            newArray[i] = array[i - 1];
        }
    }
    return newArray;
}

4、结果。

5、代码(链表的方式)

static class Bucket { //跟main方法在同个类里面
    public int value = -1;
    public Bucket next;//下一次Bucket
}
public static void main(String[] args) {
        System.out.println("------开始------");
        //生成生成两份一模一样的随机数组,其中一组用系统自带的方法进行排序,到时候进行验证。
        final int number = 100000;
        int[] sortArray = new int[number];
        int[] sortArrayCopy = new int[number];
        for (int i = 0; i < sortArray.length; i++) {
            sortArray[i] = (int) (Math.random() * number);
        }
        System.arraycopy(sortArray, 0, sortArrayCopy, 0, number);//数组复制
        Arrays.sort(sortArrayCopy);

        //开始排序
        long startTime = System.currentTimeMillis();
        bucketSort2(sortArray);//桶排序(用链表的方式)
        System.out.println("花费时间:" + (System.currentTimeMillis() - startTime));

        //跟系统排序之后数组进行比较,查看是否排序成功。
        if (Arrays.equals(sortArray, sortArrayCopy)) {
            System.out.println("排序成功");
        } else {
            System.out.println("排序失败");
        }
        System.out.println("------结束------");
    }
//桶排序 最佳情况:T(n) = O(n+k)  最差情况:T(n) = O(n2)  平均情况:T(n) = O(n+k)
private static void bucketSort2(int[] array) {
    //找出array的最大值和最小值
    int max = array[0], min = array[0];
    for (int i = 1; i < array.length; i++) {
        if (array[i] > max) {
            max = array[i];
        } else if (array[i] < min) {
            min = array[i];
        }
    }
    int length = max - min + 1;//区间
    int bucketCount = length / 10;//桶的个数,如果个数为length个,就是计数排序了
    int range = (int) Math.ceil(length * 1.0 / bucketCount);//记得要Math.ceil
    Bucket[] bucketArray = new Bucket[bucketCount];
    for (int i = 0; i < bucketArray.length; i++) {
        bucketArray[i] = new Bucket();
    }
    for (int i = 0; i < array.length; i++) {
        int bucketIndex = (int) Math.floor((array[i] - min) / range);//判断array[i]是属于第几个区间的
        if (bucketArray[bucketIndex].next == null) {//第一个
            Bucket entity = new Bucket();
            entity.value = array[i];
            bucketArray[bucketIndex].next = entity;
            continue;
        }
        //将array[i]插入链表中
        Bucket pre = bucketArray[bucketIndex];//上一个
        while (pre.next != null) {
            Bucket now = pre.next;//当前的
            if (array[i] <= now.value) {//将array[i]插入上一个和当前的之间
                Bucket entity = new Bucket();
                entity.value = array[i];
                entity.next = now;
                pre.next = entity;
                break;
            } else if (now.next == null) {//最后一个也比完了,array[i]是最大的
                Bucket entity = new Bucket();
                entity.value = array[i];
                now.next = entity;
                break;
            } else {
                pre = now;//下一个
            }
        }
    }
    //排序(记得判断bucketArray[i] != null)
    int index = 0;
    for (int i = 0; i < bucketArray.length; i++) {
        Bucket pre = bucketArray[i];//上一个
        while (pre.next != null) {
            Bucket now = pre.next;
            array[index++] = now.value;
            pre = now;
        }
    }
}

6、结果

猜你喜欢

转载自blog.csdn.net/qq_36511401/article/details/102647839