7、归并排序
(1)归并排序是一个分治算法(Divide and Conquer)的一个典型实例,把一个数组分为两个大小相近(最多差一个)的子数组,分别把子数组都排好序之后通过归并(Merge)手法合成一个大的排好序的数组,归并的过程依然用扑克来解释,想象一下桌子上有两堆排好序(从小到大)的牌,每一次从两堆里面各抽取一张,比较一下两张的大小,如果两张一样大,都取出放到目标数组,否则取出较小的放到目标数组,另外一个放回原堆里面。归并排序需要额外的空间来存储临时数据,不过它的最坏运行时间都是O(nlogn)。
(2)实例:

(3)用java实现
- public class MergeSort {
- /**
- * 归并排序
- * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
- * 时间复杂度为O(nlogn)
- * 稳定排序方式
- * @param nums 待排序数组
- * @return 输出有序数组
- */
- public static int[] sort(int[] nums, int low, int high) {
- int mid = (low + high) / 2;
- if (low < high) {
- // 左边
- sort(nums, low, mid);
- // 右边
- sort(nums, mid + 1, high);
- // 左右归并
- merge(nums, low, mid, high);
- }
- return nums;
- }
- public static void merge(int[] nums, int low, int mid, int high) {
- int[] temp = new int[high - low + 1];
- int i = low;// 左指针
- int j = mid + 1;// 右指针
- int k = 0;
- // 把较小的数先移到新数组中
- while (i <= mid && j <= high) {
- if (nums[i] < nums[j]) {
- temp[k++] = nums[i++];
- } else {
- temp[k++] = nums[j++];
- }
- }
- // 把左边剩余的数移入数组
- while (i <= mid) {
- temp[k++] = nums[i++];
- }
- // 把右边边剩余的数移入数组
- while (j <= high) {
- temp[k++] = nums[j++];
- }
- // 把新数组中的数覆盖nums数组
- for (int k2 = 0; k2 < temp.length; k2++) {
- nums[k2 + low] = temp[k2];
- }
- }
- // 归并排序的实现
- public static void main(String[] args) {
- int[] nums = { 2, 7, 8, 3, 1, 6, 9, 0, 5, 4 };
- MergeSort.sort(nums, 0, nums.length-1);
- System.out.println(Arrays.toString(nums));
- }
- }
8、基数排序
(1)基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
(2)实例:
(3)用java实现
基数排序的另外一种实现方法public static void radixSort(int[] array){ //首先确定排序的趟数; int max=array[0]; for(int i=1;i<array.length;i++){ if(array[i]>max){ max=array[i]; } } int time=0; //判断位数; while(max>0){ max/=10; time++; } //建立10个队列; LinkQueue<Integer>[] queue=new LinkQueue[10]; for(int i=0;i<10;i++){ queue[i]=new LinkQueue<Integer>(); } //进行time次分配和收集; for(int i=0;i<time;i++){ //分配数组元素; for(int j=0;j<array.length;j++){ //得到数字的第time+1位数; queue[array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i)].enQueue(array[j]); } int count=0;//元素计数器; //收集队列元素; for(int k=0;k<10;k++){ while(queue[k].size()>0){ array[count]=(Integer) queue[k].deQueue().getElement(); count++; } } } }import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * * @author lyx * @下午2:15:41 * @TODO: * 基数排序——多关键字排序 * 多关键字排序的思路是将待排数据里德排序关键字拆分成多个排序关键字; * 第1个排序关键字,第2个排序关键字,第3个排序关键字...然后,根据子关键 * 字对待排序数据进行排序。 * * 多关键字排序时有两种解决方案: *(1)最高位优先法(MSD)(Most Significant Digit first) *(2)最低位优先法(LSD)(Least Significant Digit first) * * 例如,对如下数据序列进行排序。 * 192,221,12,23 * 可以观察到它的每个数据至多只有3位,因此可以将每个数据拆分成3个关键字: * 百位(高位)、十位、个位(低位)。 * 如果按照习惯思维,会先比较百位,百位大的数据大,百位相同的再比较十位, *十位大的数据大;最后再比较个位。人得习惯思维是最高位优先方式。如果按照人 *得思维方式,计算机实现起来有一定的困难,当开始比较十位时,程序还需要判断 *它们的百位是否相同--这就认为地增加了难度,计算机通常会选择最低位优先法。 * 基数排序方法对任一子关键字排序时必须借助于另一种排序方法,而且这种排序方法必须是稳定的。 *对于多关键字拆分出来的子关键字,它们一定位于0-9这个可枚举的范围内,这个范围不大,因此用桶式排序效率非常好。 *对于多关键字排序来说,程序将待排数据拆分成多个子关键字后,对子关键字排序既可以使用桶式排序,也可以使用任何一种稳定的排序方法。 */ /** * * 基数排序是一种当关键字为整数类型时非常高效的排序方法。 * 基本思想是:设待排序的数据元素关键字是m位d进制整数(不足m位的关键字在高位补0) * 设置d个桶,令其编号分别为0,1,2.... d-1 * 首先,按关键字最低位的数值一次把各数据元素放到相应的桶中 * 然后,按照桶号从小到大和进入桶中数据元素的先后次序收集分配在各桶中的数据元素, * 这样就形成了数据元素集合的一个新的排列,此为一次基数排序。重复m次,就得到了排好序 * 的数据元素序列 * */ /** * 基数排序:要求进出桶中的数据元素序列满足先进先出的原则,桶实际上就是队列 * 队列分为顺序队列和链式队列。 */ / public class RadixSort { public static int[] asc( int[] array ){ int index = 1; // 最大数字位数(如100为3位数字,2000为4位数字) // 取得最大数字位数 for( int i = 0 ; i < array.length ; i++ ){ int length = Integer.toString(array[i]).length(); if( index < length ){ index = length; } } return sort( array , 0 , index); } /** * @param array 数组 * @param exponent 起始位数(代码中实际意义为:10的exponent次方。起始位数即为10的0次方,所以为个位) * @param index 最大数字位数(如100为3位数字,2000为4位数字) * @return */ private static int[] sort( int[] array , int exponent , int index){ int length = array.length; // 此处我选择用List,而不是int[n][n]的数组 List<List<Integer>> list = new ArrayList<List<Integer>>(); for( int i = 0 ; i < 10 ; i++ ){ list.add(new ArrayList<Integer>()); } // 按照各个位数排序统计 for( int i = 0 ; i < length ; i++ ){ int num = 0; String str = Integer.toString(array[i]); if( str.length() - exponent - 1 >= 0 ) num = Integer.parseInt(str.substring(str.length() - exponent - 1, str.length() - exponent)); list.get(num).add(array[i]); } // 串起桶中数据 for( int k = 0 , i = 0 ; i < list.size() ; i++ ){ for( int j = 0 ; j < list.get(i).size() ; j++ ){ array[ k++ ] = list.get(i).get(j); } } // 若还有更高位数,则按照下一位数进行排序 if( index == ++exponent ){ return array; }else{ return sort( array , exponent , index ); } } }

本文详细介绍了归并排序和基数排序的分治算法思想,通过扑克牌实例直观解释了归并排序过程,并提供Java实现代码。同时,探讨了基数排序的基本思想、实例和Java实现,特别强调了排序的趟数和队列的使用。

166

被折叠的 条评论
为什么被折叠?



