桶排序和计数排序

桶排序和计数排序都是一种排序效率比较高的排序算法,桶排序当桶的个数与n接近时的时间复杂度是O(n),计数排序的时间复杂度是O(n+k)。

package com.program;
import java.util.ArrayList;
import java.util.List;
public class Sort {
private int findMin(int[] data) {
        int min = data[0];
        int minIndex = 0;
        for (int i = 1; i < data.length; i++) {
            if (data[i] < min) {
                min = data[i];
                minIndex = i;
            }
        }
        return data[minIndex];
    }

    private int findMax(int[] data) {
        int max = data[0];
        int maxIndex = 0;
        for (int i = 1; i < data.length; i++) {
            if (data[i] > max) {
                max = data[i];
                maxIndex = i;
            }
        }
        return data[maxIndex];
    }

    /**
     * 桶排序
     * 用途:一般用作外部排序,比如有10G的订单数据,但是内存只有100M大小内存,这个时候就用到桶排序,
     * 步骤:1.扫描一遍整个文件,看整个订单数据的订单金额所处的范围大小
     * 2.按照订单金额分成m个桶,然后将所有的订单存放到这m个桶里(这样每个桶里存放的都是同一区间的数据了)
     * 3.对这m个桶进行内部排序(时间复杂度O(nlogn)
     * 4.将m个桶里的数据按照顺序拿出来,over了。
     * 时间复杂度:当整体数据n与桶的个数接近时,时间复杂度能达到O(n)
     * 空间复杂度:未知,和数据是否均匀分布有关
     */
    public void bucketSort(int[] data) {
        if (data == null || data.length == 0) {
            return;
        }
        int n = data.length;
        int min = findMin(data);
        //这里假设数据量不是很多的情况,桶的个数m与n相同
        //我这里为了简单直接用List来表示了,实际上也可以用链表来表示桶,或者数组扩容也行。
        ArrayList<ArrayList<Integer>> bucket = new ArrayList<>(n);
        for (int j = 0; j < n; j++) {
            bucket.add(new ArrayList<Integer>());
        }
        for (int i = 0; i < data.length; i++) {
            //找到这个数所在的桶的位置(这里因为桶的个数与n相同,所以每个桶只存一个数,那么计算就很简单了)
            int k = (data[i] - min) % data.length;
            bucket.get(k).add(data[i]);
        }
        //遍历所有的桶,拿出数据,over了
        for (int l = 0; l < bucket.size(); l++) {
            ArrayList<Integer> list = bucket.get(l);
            if (list.size() > 0) {
                for (int m = 0; m < list.size(); m++) {
                    System.out.print(list.get(m) + " ");
                }
            }
        }
    }

    /**
     * 计数排序
     * 计数排序其实是桶排序的特定情况,就是每个桶里存放的都是一样的数据,就避免了桶内排序
     * 用途:比如查找高考排名,因为分数在一个正常的范围内,并且是正整数。或者对公司内员工年龄排序,这些都是在一个正常范围内
     * 思路:
     * 1.找出n个数A[n]中最大的一个数k,
     * 2.创建一个空的数组B[k+1],其中数组下标表示n中的值,遍历A[n],得到每个数的个数,(这个就是计数数组了)
     * 3.对计数数组进行按顺序求和,就能得到<=某个数的个数了
     * 4.创建一个空的数组C[n]用来存放排序后的数,
     * 5.根据计数数组来将对应的数存放到C[n]中,具体怎么存放,请看代码
     * 时间复杂度&空间复杂度都是O(n)+O(k),其中k是计数数组个数
     */
    public void countingSort(int[] data) {
        if (data == null || data.length == 0) {
            return;
        }
        System.out.println("原始数组为:");
        printArray(data);
        int max = findMax(data);
        //创建一个计数数组
        int[] countArray = new int[max + 1];
        for (int i = 0; i < data.length; i++) {
            int value = data[i];
            countArray[value] += 1;
        }
        System.out.println("计数数组为:");
        printArray(countArray);
        //对计数数组按顺序求和,得到小于某个数的个数
        //这种时间复杂度要稍微高一点
        /*for (int j = countArray.length - 1; j >= 0; j--) {
            for (int k = j - 1; k >= 0; k--) {
                countArray[j] += countArray[k];
            }
        }*/
        //换下面这种方式
        for (int j = 1; j < countArray.length; j++) {
            countArray[j] += countArray[j - 1];
        }

        //经过这一步之后就能知道自己的分数排名了
        System.out.println("计数数组按顺序求和之后:");
        printArray(countArray);
        //从后往前(从前往后也行,只不过不是稳定的排序了)挨个儿从原始数组中取出数x,
        //然后在计数数组中查找x前面有y个数,
        //然后将x存放到resultArray[y]的位置
        //其实计数排序的核心思想就在这里,就是找到某个数前面到底有多少个数,然后插入到对应位置即可。
        int[] resultArray = new int[data.length];
        for (int m = data.length - 1; m >= 0; m--) {
            int value = data[m];
            int position = countArray[value];
            resultArray[position - 1] = value;
            countArray[value] -= 1;
        }
        System.out.println("排序之后的数组:");
        printArray(resultArray);

    }

    private void printArray(int[] data) {
        if (data == null || data.length == 0) {
            return;
        }
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        //int[] data = new int[]{10, 11, 12, 13, 14, 3, 5, 6, 7, 8};
        //int[] data = new int[]{11, 24, 25, 10, 9, 11, 24, 25, 10, 9, 11, 67, 11, 67, 11, 24, 25, 11, 24, 25, 10, 9, 11, 67, 10, 9, 11, 67, 11, 24, 25, 10, 9, 11, 67, 33, 89, 11, 45, 22, 35, 56, 22, 23};
        Sort sort = new Sort();
        //sort.bubbleSort(data);
        //sort.insertionSort(data);
        //sort.selectionSort(data);
        //int[] data = new int[]{1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
        //sort.merge(data, 0, 4, 5, data.length - 1);
        //sort.mergeSort(data, 0, data.length - 1);
        int[] data = new int[]{2, 5, 3, 0, 2, 3, 0, 3};
        //sort.quickSort(data, 0, data.length - 1);
        //for (int i = 0; i < data.length; i++) {
        //    System.out.print(data[i] + " ");
        //}
        sort.countingSort(data);
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 概述:排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    每天刷两次牙阅读 3,747评论 0 15
  • (一) 艾顿森林里,住着兔子兄弟志麻和多尼。 兔子们每天忙着在森林里往来穿梭,采集食物。甜嫩多汁的草丛和各种植物块...
    Christine7718阅读 669评论 4 4
  • 时光飞逝,2018年来到。在过去的2017是收获的一年,成长的一年。今天是最后一天。在这里祝大家新年快乐...
    格姐阅读 496评论 0 0
  • 朋友圈里刷了一段小视频,朋友打的小甜甜三字。 这姑娘长的比我好看多了。 接着晚上老公拿出这段视频给我看,嘴里...
    年初七阅读 338评论 0 0
  • 今天跟老婆带着两个孩子回娘家,在高速公路上接到岳父电话,说上午要去鱼塘买鱼,让我们下了高速直接去鱼塘那边玩。小孩子...
    dingwen_e950阅读 267评论 1 1