前言

在比较排序的算法中,快速排序的性能最佳,时间复杂度是O(N*logN).因此,在使用比较排序时,时间复杂度的下限就是O(N*logN)。而桶排序的时间复杂度是O(N+C),因为它的实现并不是基于比较实现的,而是基于映射函数实现的。

桶排序

桶排序工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。

桶排序利用函数的映射关系,减少了几乎所有的比较工作。实际上,桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量数据分割成了基本有序的数据块(桶)。然后只需要对桶中的少量数据做先进的比较排序即可。

时间复杂度和空间复杂度分析

    对于N个待排数据,M个桶,平均每个桶[N/M]个数据的桶排序平均时间复杂度为:
               O(N)+O(M*(N/M)*log(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM)
    当N=M时,即极限情况下每个桶只有一个数据时。桶排序的最好效率能够达到O(N)。
 
    空间负载度:O(N+M)
 
下面以leetcode164. Maximum Gap为例具体讲解桶排序的实现
   题目要求:
   Given an unsorted array, find the maximum difference between the successive elements in its sorted form.

Try to solve it in linear time/space.  //题目要求我们使用线性的时间

Return 0 if the array contains less than 2 elements.

You may assume all elements in the array are non-negative integers and fit in the 32-bit signed integer range.

解题思路:

首先对数组进行桶排序,然后找出最大的gap。

首先明确,最大的gap肯定是后桶的最小值减去前桶的最大值,注意此处说的桶均为有效桶,即不包括空桶。

因此无需进行桶内排序,仅需记录每个有效桶的最大值和最小值即可。

为何最大的gap肯定是后桶的最小值减去前桶的最大值?

假设有数组x[1...n],其中最大的元素为max,最小元素为min,将左闭右开的实数区间[min,max)划分为n-1个等长的子区间(桶),每个子区间也是左闭右开的,我们用len来表示每个子区间的长度。除去max和min,剩下的n-2个数,每个数都属于其中一个桶。对于同一个桶的两个数,因为桶是左闭右开的,所以他们的距离肯定是小于len的。然后,关键的一点是,n-2个数放进n-1个桶,由抽屉原理可以知道,肯定有一个桶是空的,所以,距离最远的相邻的两个数,肯定是属于两个不同的桶。于是,我们可以把每个桶都扫描一次,相邻最远的两个数,必定其中一个是某个桶里的最大值,另一个是另一个桶里的最小值。

代码:

public class Solution {
public int maximumGap(int[] num) {
if(num == null || num.length<2){
return 0;
}
int maxval = Integer.MIN_VALUE;
int minval = Integer.MAX_VALUE;
//求解数组最值
for(int i=0; i<num.length; i++){
if(num[i]>maxval) maxval = num[i];
if(num[i]<minval) minval = num[i];
} //数组内的元素值相同
if(minval==maxval){
return 0;
} //数组仅有两个元素
if(num.length==2){
return maxval-minval;
} int len = (int)Math.ceil((double)(maxval-minval)/(num.length-1)); //求解桶间差值,向上取整
int n = (maxval-minval)/len; int maxBuk[] = new int[n+1];
int minBuk[] = new int[n+1]; Arrays.fill(maxBuk,Integer.MIN_VALUE);
Arrays.fill(minBuk,Integer.MAX_VALUE); //桶映射
for(int val:num){
int temp = (val-minval)/len;
maxBuk[temp] = Math.max(val,maxBuk[temp]);
minBuk[temp] = Math.min(val,minBuk[temp]);
} //求解最大gap,最大差值位于后桶的min-前桶的max
int gap = 0;
int pre = maxBuk[0];
for(int i=1; i<=n; i++){
if(maxBuk[i]==Integer.MIN_VALUE && minBuk[i]==Integer.MAX_VALUE){ //忽略空桶
continue;
}
gap = Math.max(gap,minBuk[i]-pre);
pre = maxBuk[i];
} return gap; }
}

线性时间的排序算法--桶排序(以leetcode164. Maximum Gap为例讲解)的更多相关文章

  1. 使用 js 实现十大排序算法: 桶排序

    使用 js 实现十大排序算法: 桶排序 桶排序 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  2. Java排序算法——桶排序

    文字部分为转载:http://hxraid.iteye.com/blog/647759 对N个关键字进行桶排序的时间复杂度分为两个部分: (1) 循环计算每个关键字的桶映射函数,这个时间复杂度是O(N ...

  3. 排序算法-桶排序(Java)

    package com.rao.sort; import java.util.*; /** * @author Srao * @className BucketSort * @date 2019/12 ...

  4. 十大经典排序算法+sort排序

    本文转自:十大经典排序算法,其中有动图+代码详解,本文简单介绍+个人理解. 排序算法 经典的算法问题,也是面试过程中经常被问到的问题.排序算法简单分类如下: 这些排序算法的时间复杂度等参数如下: 其中 ...

  5. 计数排序和桶排序(Java实现)

    目录 比较和非比较的区别 计数排序 计数排序适用数据范围 过程分析 桶排序 网络流传桶排序算法勘误 桶排序适用数据范围 过程分析 比较和非比较的区别 常见的快速排序.归并排序.堆排序.冒泡排序等属于比 ...

  6. 计数排序与桶排序python实现

    计数排序与桶排序python实现 计数排序 计数排序原理: 找到给定序列的最小值与最大值 创建一个长度为最大值-最小值+1的数组,初始化都为0 然后遍历原序列,并为数组中索引为当前值-最小值的值+1 ...

  7. 【JS面试向】选择排序、桶排序、冒泡排序和快速排序简介

    新年伊始,又到了金三银四的时候了.面对前端越来越多的算法面试题,我简单的整理了一下几种比较常见的数组排序方式,分别介绍其基本原理和优劣势.(ps:才疏学浅,希望大家可以在issues下面指出问题) 选 ...

  8. 经典排序算法 - 高速排序Quick sort

    经典排序算法 - 高速排序Quick sort 原理,通过一趟扫描将要排序的数据切割成独立的两部分,当中一部分的全部数据都比另外一部分的全部数据都要小,然后再按此方法对这两部分数据分别进行高速排序,整 ...

  9. 排序算法--希尔排序(Shell Sort)_C#程序实现

    排序算法--希尔排序(Shell Sort)_C#程序实现 排序(Sort)是计算机程序设计中的一种重要操作,也是日常生活中经常遇到的问题.例如,字典中的单词是以字母的顺序排列,否则,使用起来非常困难 ...

随机推荐

  1. [BZOJ2795][Poi2012]A Horrible Poem

    2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 261  Solved: 150[Subm ...

  2. Spring MVC过滤器-字符集过滤器(CharacterEncodingFilter)

    spring的字符集过滤通过用于处理项目中的乱码问题,该过滤器位于org.springframework.web.filter包中,指向类CharacterEncodingFilter,Charact ...

  3. GO语言练习:值与引用

    1.代码 2.运行 package main import "fmt" func testValue(){ fmt.Println("for value") v ...

  4. 使用swf与swc引入资源的区别[as3]

    SWF用于运行时加载,而SWC在编译时用做外部库.使用SWC库,只有当类被引用,才编译入主程序中. getDefinition: 在使用swc库方式引入资源时,getDefinitionByName反 ...

  5. Linux_vi编辑器

    一.vi/vim编辑器的三种模式 1. 命令模式 2. 插入模式 3. 最后行模式 eRrsr@hadoop09-linux tmp]$ vi vi.txt # 命令行 start to write ...

  6. [转载]学习VC MFC开发必须了解的常用宏和指令————复习一下

    1.#include指令  包含指定的文件 2.#define指令   预定义,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善.背后一长串”的宏,它本身并不在编译过程中进行,而 ...

  7. c#面向对象基础 封装、继承

    一.封装 这是一种隐藏的特性.可以用一个公式来展示类的封装特性: 封装的类=数据  +  对此数据进行的操作(即算法) 通俗的说,封装就是:包起外界不必要知道的东西,只向外界展露可供展示的东西. 在面 ...

  8. log4net 添加自定义日志到数据库

    添加操作日志到数据库举例: (一)建立数据库的操作日志表,如下我建立了一个简单的日志表 (二)配置文件中的配置如下 <log4net> <!--错误日志记录数据库--> < ...

  9. GROUP BY 與 Null 值

    若群組資料行包含了 Null 值,該資料列將變成結果中的一個群組.若群組資料行內包含了多個 Null 值,Null 值將放入單一群組內.此行為定義於 SQL-2003 標準之中. Product 資料 ...

  10. poj2305-Basic remains(进制转换 + 大整数取模)

    进制转换 + 大整数取模一,题意: 在b进制下,求p%m,再装换成b进制输出. 其中p为b进制大数1000位以内,m为b进制数9位以内二,思路: 1,以字符串的形式输入p,m; 2,转换:字符串-&g ...