做导航网站不侵权吗,淘宝搜索指数,大连甘井子区租房,保定企业网站建设桶排序
计数排序#xff08;基于统计#xff09;
要求数据是有限的#xff0c;和数据状况有关#xff0c;比如对于200个人统计他们的年龄分布#xff0c;这个时候需要申请200个桶#xff0c;因此对于输入数据的规模有限制#xff0c;如果输入规模是不定的#xff0c;…桶排序
计数排序基于统计
要求数据是有限的和数据状况有关比如对于200个人统计他们的年龄分布这个时候需要申请200个桶因此对于输入数据的规模有限制如果输入规模是不定的空间申请就会很麻烦。
基数排序
思想
要求排序的数字都是十进制的数字找到最高位的数字对于其中不满足位数的数字前面补0例如【1002334】就需要改写成【100023034】的形式。准备和数字相同数目的桶类比于先进先出的队列所有数字按照个位数字进桶然后按照从左往右的次序依次往出倒数字如果一个桶内有多个数字按照次序队列倒数再按照十位数字进桶原理和先前类似倒出再按照百位数字进桶出桶。最后的次序是从小到大的。
落地 初始数组为【23133242314】申请两个栈一个为count一个是help。count按照次序分别是【0123456789】这个用于统计对应的数字的个数比如上面这个例子的话个位是3的个数有4个个位是4的个数有3个。而help指定的是数组中元素的个数。此时一个6个元素所以将help的大小设置为6。 统计完对应的数字数字之后得到的count为【0004200000】对其进行加工对应元素的位置等于自身的值前面的元素值。如果是0号位置就是本身1号就是002号是003号是404号是405号是60依次类推剩余元素的值都是6。经过加工后的count数组含义就是小于等于相应位置上元素的个数。比如小于等于3的有三个元素小于等于56789的有6个元素。操作过程
从右往左遍历第一个元素是14个位数小于等于6的有6个所以将14填写在help的5位置上并且将count数组中的4对应的6减1变成5。下一个元素是23个位元素对应的是3查询count数组小于等于3的元素有四个因此将23填写在help数组的3号位置count中3号位置的4减1下一个元素是24 个位元素对应的是4查询count数组小于等于4的元素有5个因此将24填写在help数组的4号位置count中4号位置的5减1下一个元素是3 个位元素对应的是3查询count数组小于等于3的元素有3个因此将3填写在help数组的2号位置count中3号位置的3减1下一个元素是13 个位元素对应的是3查询count数组小于等于3的元素有2个因此将3填写在help数组的1号位置count中3号位置的2减1下一个元素是23 个位元素对应的是3查询count数组小于等于3的元素有1个因此将3填写在help数组的0号位置count中3号位置的1减1
完整代码
package class03;import java.util.Arrays;public class Code02_RadixSort {// only for no-negative valuepublic static void radixSort(int[] arr) {if (arr null || arr.length 2) {return;}radixSort(arr, 0, arr.length - 1, maxbits(arr));}public static int maxbits(int[] arr) {int max Integer.MIN_VALUE;for (int i 0; i arr.length; i) {max Math.max(max, arr[i]);}int res 0;while (max ! 0) {res;max / 10;}return res;}// arr[begin..end]排序public static void radixSort(int[] arr, int L, int R, int digit) {final int radix 10;int i 0, j 0;// 有多少个数准备多少个辅助空间int[] bucket new int[R - L 1];for (int d 1; d digit; d) { // 有多少位就进出几次// 10个空间// count[0] 当前位(d位)是0的数字有多少个// count[1] 当前位(d位)是(0和1)的数字有多少个// count[2] 当前位(d位)是(0、1和2)的数字有多少个// count[i] 当前位(d位)是(0~i)的数字有多少个int[] count new int[radix]; // count[0..9]for (i L; i R; i) {j getDigit(arr[i], d);count[j];}for (i 1; i radix; i) {count[i] count[i] count[i - 1];}for (i R; i L; i--) {j getDigit(arr[i], d);bucket[count[j] - 1] arr[i];count[j]--;}for (i L, j 0; i R; i, j) {arr[i] bucket[j];}}}public static int getDigit(int x, int d) {return ((x / ((int) Math.pow(10, d - 1))) % 10);}// for testpublic static void comparator(int[] arr) {Arrays.sort(arr);}// for testpublic static int[] generateRandomArray(int maxSize, int maxValue) {int[] arr new int[(int) ((maxSize 1) * Math.random())];for (int i 0; i arr.length; i) {arr[i] (int) ((maxValue 1) * Math.random());}return arr;}// for testpublic static int[] copyArray(int[] arr) {if (arr null) {return null;}int[] res new int[arr.length];for (int i 0; i arr.length; i) {res[i] arr[i];}return res;}// for testpublic static boolean isEqual(int[] arr1, int[] arr2) {if ((arr1 null arr2 ! null) || (arr1 ! null arr2 null)) {return false;}if (arr1 null arr2 null) {return true;}if (arr1.length ! arr2.length) {return false;}for (int i 0; i arr1.length; i) {if (arr1[i] ! arr2[i]) {return false;}}return true;}// for testpublic static void printArray(int[] arr) {if (arr null) {return;}for (int i 0; i arr.length; i) {System.out.print(arr[i] );}System.out.println();}// for testpublic static void main(String[] args) {int testTime 500000;int maxSize 100;int maxValue 100000;boolean succeed true;for (int i 0; i testTime; i) {int[] arr1 generateRandomArray(maxSize, maxValue);int[] arr2 copyArray(arr1);radixSort(arr1);comparator(arr2);if (!isEqual(arr1, arr2)) {succeed false;printArray(arr1);printArray(arr2);break;}}System.out.println(succeed ? Nice! : Fucking fucked!);int[] arr generateRandomArray(maxSize, maxValue);printArray(arr);radixSort(arr);printArray(arr);}}稳定性 相同元素排序保证先后顺序同样数值的个体之间如果不因为排序而改变相对次序这个排序就是有稳定性的否则则没有基于比较的排序一般都是不稳定的基数排序按照个位、十位、百位上的元素的大小进行相对次序的排列和计数排序统计相同数值的元素出现的次数押入对应的元素组成的数据栈利用栈先入后出的特性保持元素的相对次序参考上文统计0-200员工年龄分布问题是稳定的不具备稳定性的排序选择排序、快速排序 和 堆排序具备稳定性的排序 冒泡排序、插入排序 、归并排序 、一切桶排序思想下的排序计数排序和基数排序目前没有 时间复杂度为O(N*logN) 额外时间复杂度O(1) 又稳定的排序稳定性 主要体现在 非基础类型数据的排序比如对自定义结构体学生类型{年龄、班级},先按照年龄排序再按照班级进行排序
分析
桶排序思想下的排序都是不基于比较的排序 时间复杂度为O(N)额外空间负载度O(M) 应用范围有限需要样本的数据状况满足桶的划分
汇总
快速排序不是基于比较的排序时间空间稳定性备注选择排序O(N^2)O(1)不稳定{5,5,5,3} 3和第一个5交换不稳定冒泡排序O(N^2)O(1)稳定 插入排序O(N^2)O(1)稳定{3,4,4,5}新插入元素4不可以越过与其相等元素的左边即元素相等的话只会排在相等区域的最后位置归并排序O(N*logN)O(N)稳定{1,1,2,2}{1,1,2,2}左边和右边进行比较拼接的时候先拷贝左边的元素再拷贝右边的元素快速排序O(N*logN)O(logN)不稳定{3,4,5,6,6,6,6,6,|2,333} 2会和第一个6进行交换打破了相对次序堆排序O(N*logN)O(1)不稳定树状结构{5,5,5,5,6}第一个5会和6交换不稳定桶排序基数/计数O(N)O(M)稳定非比较
归并、快排、和堆排序最为关键不在乎稳定性的前提小使用快速排序最好时间最快实验可知需要稳定性的话使用归并排序在乎额外空间的话使用堆排序
常见的坑
归并排序的额外空间复杂度可以变为O(1)但是会失去稳定性的优势详见《归并排序内部缓冲法》原地归并排序很垃圾会将时间复杂度变成O(N^2)快速排序也可以做到稳定性但是非常难详见《01 stable sort》所有的改进都不重要 目前没有 时间复杂度为O(N*logN) 额外空间复杂度为 O(1) 又稳定的排序将一个数组中所有的奇数移到数组的左边所有的偶数移到数组的右边。保持相对次序不变的同时要是时间复杂度为O(N),空间复杂度为O(1)。这个没法做
对于排序的改进优化
充分利用O(N*logN)和O(N^2)的排序的各自优势 数据规模很大的时候使用快速排序当数据规模减少数据项在60以内的时候该换成插入排序同时使用快速和插入两种方法能进一步提高效率减少时间复杂度。
稳定性考虑
如果输入的数据是基础类型使用快速排序如果输入的类型是自定义的类型使用插入、归并这些可以保证稳定性的排序方法Java里面自带的排序算法即array.sort如果是常规类型比如int的话是使用快速排序提高速度如果是自定义的类型比如学生的年龄结构体定义的字段会使用桶排序保证比较的稳定性。即算法看重时间复杂度 空间复杂度和稳定性数值相等的元素排序保证先后次序不变基础类型按照数值传递非基础类型比如自定义结构体按照引用传递具体体现在integer这个类型127相等128就不等了。因为128以上就作为不同内存了也就是按照引用比较了