数据结构与算法系列十六(桶排序)

131 阅读4分钟

到目前为止,我们已经知道了时间复杂度O(n^2) 的排序算法:冒泡排序、插入排序、选择排序;还知道了时间复杂度是O(nlogn) 排序算法:归并排序、快速排序

事实上归并排序、快速排序算法已经可以满足我们大多数的排序需求场景了。还有那么一种场景,我们来假设一下: 某一个社区,有100万人口,我们需要按照年龄大小进行排序,统计该社区的人口年龄分布。这里抓一下关键: 给100万人排序,实际中可能是1000万,或者说以亿为单位的数据量进行排序。

你有想到了什么吗?就是待排序的数据量太大,那么不管是时间复杂度O(n^2) 的排序算法,还是说时间复杂度O(nlogn) 的排序算法,都显得不够高效。有没有更高效的排序算法呢?

答案是:有。它就是我们今天的主角:桶排序

#考考你:
1.你知道桶排序适合的业务场景,以及实现思想吗
2.你能用java实现桶排序吗

案例

桶排序实现思想

关于桶排序,它适合的业务场景,我们记住一句话就够了:数据量大、数据分布相对均匀

我们举一个例子:

1.给100万用户,按照年龄进行排序,年龄取值范围:0-120

2.满足业务场景,数据量大:100万;数据分布均匀:年龄取值范围0-120岁

实现思想:

1.根据数据取值范围,划分桶: 比如把0-10岁、11-20岁、21-30岁...依次分桶

2.分桶以后,保证桶之间有序。这里按照年龄范围分桶,保证桶之间有序

3.在每个桶内,通过快速排序算法,进行排序

4.最终按照桶顺序进行访问,得到排好序的数据

5.桶排序是一个高效的排序算法,它的时间复杂度是:O(n)

桶排序代码实现

主体代码

package com.anan.algorithm.sort;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * 桶排序:
* */
public class BucketSort {

public static void main(String[] args) {
    // 1.定义待排序数组
    Integer[] array = {1,6,3,4,2,5,9,8,10,7,11,20,17,16,15,18,19,2,3,1,9,8,6};
    int n = array.length;
    System.out.println("1.待排序数组:" + Arrays.asList(array));

    // 2.分两个桶,桶1:1-10岁,桶2:11-20岁
    System.out.println("1.数组元素最小值:1,最大值:20,分两个桶:bucket_1,bucket_2");
    ArrayList<Integer> bucket_1 = new ArrayList<Integer>(10);
    ArrayList<Integer> bucket_2 = new ArrayList<Integer>(10);

    // 将array数组的数据,分别填充到桶中
    for(int i = 0; i < n; i++){
       if(array[i] >=1 && array[i] <= 10){
            bucket_1.add(array[i]);
       }else{
            bucket_2.add(array[i]);
       }
     }

     // 打印分桶后的数据
     System.out.println("--------------------------华丽丽分割线------------------------------");
     System.out.println("2.排序前,桶bucket_1的大小:" + bucket_1.size() + ",元素内容:" + bucket_1);
     System.out.println("3.排序前,桶bucket_2的大小:" + bucket_2.size() + ",元素内容:" + bucket_2);

     // 3.通过快速排序,给每个桶排序
     System.out.println("--------------------------华丽丽分割线------------------------------");
     System.out.println("4.在每一个桶内进行快速排序:");
     sort(bucket_1);
     sort(bucket_2);

    // 打印分桶排序后的数据
    System.out.println("5.排序后,桶bucket_1的大小:" + bucket_1.size() + ",元素内容:" + bucket_1);
    System.out.println("6.排序后,桶bucket_2的大小:" + bucket_2.size() + ",元素内容:" + bucket_2);

    }
}

快速排序部分代码

排序入口

/**
* 排序入口方法
* @param list
*/
public static void sort(ArrayList<Integer> list){
   // 如果数据规模小于等于1,则不需要排序
   if(list == null ||
        list.size() <=1){
      return;
   }

   // 将list数据拷贝到数组,方便操作
   Integer[] array = new Integer[list.size()];
   for (int i = 0; i < array.length; i++) {
       array[i] = list.get(i);
   }

   // 开始快速排序
   quickSort(array,0,array.length - 1);

   // 清空原list数据,再把排好序的结果数据拷贝回list
   list.clear();
   for (int i = 0; i < array.length; i++) {
        list.add(array[i]);
    }
}

快速排序主体代码

/**
* 快速排序
* @param array
* @param low
* @param high
*/
public static void quickSort(Integer[] array,int low,int high){
  // 终止条件
  if(low >= high){
        return;
  }

 // 分区函数寻找pivot
 int mid = partition(array,low,high);

  // 低位递归排序
  quickSort(array,low,mid-1);

  // 高位递归排序
  quickSort(array,mid+1,high);
}

快速排序分区函数:

// 分区函数
public static int partition(Integer[] a,int low,int high){
   // 选取第一个元素作为pivot(分区点)
   int pivot = a[low];

   // 循环处理low<high
   while (low < high){
     // 从右往左,将小于pivot的数据,放入左边
     while(low < high &&
         a[high] >= pivot){
         high -= 1;
      }
      a[low] = a[high];

      // 从左往右,将大于pivot的数据,放入右边
      while(low < high &&
                    a[low] <= pivot){
           low += 1;
      }

      a[high] = a[low];

   }

   // 找到分区点
   a[low] = pivot;
   return low;
}

image.png