排序算法
对于一种算法,一般从如下3个方面来衡量算法的优劣。
时间复杂度
空间复杂度
稳定性
时间复杂度:执行算法所需要的计算工作量;
空间复杂度:执行算法所需要的内存空间。
稳定性:假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的。
目录
1 分类
排序大的可分为2种,内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序。如果排序过程中需要使用外存,则称为外排序。
内排序可以分为以下几类:
(1).插入排序:直接插入排序、二分法排序、希尔排序
(2).选择排序:简单选择排序、堆排序
(3).交换排序:冒泡排序、快速排序
(4).归并排序
(5).基数排序
不稳定:简单选择排序、快速排序、希尔排序、堆排序
稳定:冒泡排序、直接插入排序、二分法排序、归并排序和基数排序
2 排序方法的选择
3 冒泡排序
3.1 说明
冒泡排序(Bubble Sort)是一种简单的排序算法。它重复得走访要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,直至没有再需要交换,也就是说该数列已经完成排序。
3.2 原理
(1) 比较相邻的元素,如果第一个比第二个大,就交换他们两个;
(2) 对每一对相邻元素做相同的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数;
(3) 针对所有的元素重复以上的步骤,出了最后一个;
(4) 持续每次对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较。
在最坏的情况下n个数组进行冒泡排序需要n-1趟比较
3.3 代码示例
/**
* 冒泡排序
* @authorxflig
*
*/
publicclass BubbleSort {
publicstaticvoid main(String[] args)
{
intscore[] = {100,99,90,89,87,75,69,67};
//最多做n-1趟排序
for(inti = 0; i<score.length - 1; i++)
{
//对当前无需区间score[0....score.length-i-1]进行排序(j的范围很关键)
for(intj = 0; j<score.length-i-1; j++)
{
//把小的值交换到前面
if(score[j] >score[j+1])
{
inttemp = score[j];
score[j] = score[j+1];
score[j+1] = temp;
}
}
System.out.print("第"+(i+1)+"次排序结果是:");
for(inta = 0; a<score.length;a++)
{
System.out.print(score[a]+" ");
}
System.out.println();
}
System.out.print("最终的排序结果是:");
for(inta = 0; a<score.length; a++)
{
System.out.print(score[a] + " ");
}
}
}
|
3.4 效率
时间复杂度O(N^2),适用于排序小列表;最佳情况(序列本身就是正序),时间复杂度为O(n)
3.5 改进版
3.5.1 代码示例
/**
* 冒泡排序(改进版)
* @authorxflig
*
*/
publicclass BubbleSort2 {
publicstaticvoid main(String[] args)
{
intscore[] = {67,69,75,87,89,90,99,100};
booleandidSwap;
//最多做n-1趟排序
for(inti = 0; i<score.length - 1; i++)
{
didSwap = false;
//对当前无需区间score[0....score.length-i-1]进行排序(j的范围很关键)
for(intj = 0; j<score.length-i-1; j++)
{
//把小的值交换到前面
if(score[j] >score[j+1])
{
inttemp = score[j];
score[j] = score[j+1];
score[j+1] = temp;
//如果经过
didSwap = true;
}
}
if(false == didSwap)
{
System.out.print("第"+(i+1)+"次排序结果是:");
for(inta = 0; a<score.length;a++)
{
System.out.print(score[a]+" ");
}
System.out.println();
System.out.print("最终的排序结果是:");
for(inta = 0; a<score.length; a++)
{
System.out.print(score[a] + " ");
}
return;
}
}
}
}
|
4 快速排序
4.1 说明
快速排序(QuickSort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
4.2 算法
一趟快速排序的算法是:
(1) 设置两个变量i,j,排序开始的时候i=0;j=N-1;
(2) 以第一个数组元素作为关键数据,复制给key,即key=a[0];
(3) 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值a[j],将A[j]和a[i]互换;
(4) 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的值a[i],将a[i]和a[j]互换;
(5) 重复第3,4步,直到i=j;(在3,4步中,没有找到符合条件的值,改变I,j的值,直到找到为止。找到符合条件的值,进行交换的时候,I,j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时循环结束。
4.3 代码示例
/**
* 快速排序
* @authorxflig
*
*/
publicclass QuickSort {
publicstaticvoid main(String[] args)
{
int[] data
//快速排序
quickSort(data);
//输出结果
for(inta = 0; a<data.length; a++ )
{
System.out.print(data[a] + " ");
}
}
/**
* 获取中间值
* @param data 待排序数组
* @param start 起始点
* @param end 终止点
*/
privatestaticint getMiddle(int[] data,intstart,intend)
{
//找一个中间值
inttemp = data[start];
while(start<end)
{
while(start<end&&data[end] >= temp)
{
end--;
}
data[start] = data[end];//将比中间值小的移到左侧
while(start<end&&data[start] <= temp)
{
start++;
}
data[end] = data[start];//将比中间值大的移到右侧
}
data[start] = temp;
returnstart;
}
/**
* 递归快速排序
* @param data
* @param start
* @param end
*/
publicstaticvoid _quickSort(int[] data,intstart,intend)
{
if(start<end)
{
//将数组一份为2
intmiddle = getMiddle(data, start, end);
//对左侧进行排序
_quickSort(data, start, middle);
//对右侧进行排序
_quickSort(data, middle+1,end
}
}
/**
* 快速排序
* @param data 待排序数组
*/
publicstaticvoid quickSort(int[] data)
{
if(data.length> 0)
{
_quickSort(data,0,data.length-1);
}
}
}
|
4.4 效率
快速排序是通常被认为在同数量级的排序方法中平均性能最好的。
快速排序的空间效率很好,因为每趟都能确定的元素呈指数增长。
快速排序需要使用递归,而递归需要使用栈,因此它的空间效率为O(lnN)
快读排序含跳跃式交换,因此是不稳定的排序算法。
5 直接选择排序
选择排序是常用内部排序的一种,常见的算法有直接选择排序算法和堆排序算法。
选择排序的基本思想是每次从待排序的数据中选择第n小的数据放到排序列表的第n个位置,假如共有N个数据,那么经过N-1次排序后,待排数据就变成了从小到大的顺序排列了。
5.1 说明
直接选择排序(selectionSort)是一种简单直观的排序算法。首先在未排序的序列中找到最小元素,存放到排序队列的起始位置,然后,再从剩余未排序的元素中找出最小元素,然后存放到排序队列的末尾(目前已被排序的序列)。以此类推,直到所有元素均排序完毕。
5.2 算法
假设数据放在一个数组A中,且数组的长度是N,则直接选择排序的流程如下:
(1) 从A[0]—A[N-1]中选出最小的元素,然后与A[0]交换位置;
(2) 从A[1]—A[N-1]中选出最小元素,然后与A[1]交换位置(第一步结束后,A[0]就是N个数的最小值)
(3) 从A[2]—A[N-1]中选取最小的数据,然后与A[2]交换位置(第二部结束后,A[1]就是N-1个数的最小值)
(4) 以此类推,N-1次排序后,待排序列就是从小到大的有序数列了。
5.3 代码示例
/**
* 直接选择排序
* @authorxflig
*
*/
publicclass SelectionSort {
publicstaticvoid main(String[] args)
{
intscore[] = {100,99,90,89,87,75,69,67};
for(inti = 0; i<score.length-1; i++)
{
for(intj = i+1; j<score.length ; j++)
{
if(score[i] >score[j])
{
//交换位置
inttemp = score[i];
score[i] = score[j];
score[j] = temp;
}
}
System.out.print("第"+(i+1)+"趟的排序结果是:");
for(inta = 0 ; a<score.length; a++ )
{
System.out.print(score[a] +" ");
}
System.out.println();
}
System.out.print("最终的排序结果是:");
for(inta = 0 ; a<score.length; a++ )
{
System.out.print(score[a] +" ");
}
}
}
|
5.4 效率
时间复杂度是O(n*n),适用于排序小的列表
5.5 改进版
上面的直接排序算法中,有很大的改进空间,因为在n-1趟比较中,每趟比较的目的就是选出本趟中的最小元素而放在第一位,所以我们在每趟比较中找出最小的元素后只与第一位的元素进行一次交换,而不是在每次比较中,一旦发现某个数比第一个数小就立即交换。
/*
* fileName:SelectionSort.java
*
* Description: 直接选择排序(改进版)
* author:@xflig
* Date: 2017-9-22 上午10:13:50
*
*/
public class SelectionSort
{
public static void main( String[] args )
{
//构造测试数据
int[] data = {14,13,11,15,12};
//遍历实现
for(int i=0; i < data.length-1; i++)
{
int minIndex = i;
for(int j=i + 1; j < data.length; j++)
{
if(data[minIndex] > data[j])
{
minIndex = j;
}
//如果 minIndex 发生变化了,则进行交换顺序
}
if(minIndex != i)
{
int temp = data[i];
data[i] = data[minIndex];
data[minIndex] = temp;
}
System.out.print("第"+(i+1)+"趟排序的结果是:");
for(int a = 0; a < data.length; a++)
{
System.out.print(data[a]+" ");
}
System.out.println();
}
System.out.print("最终排序的结果是:");
for(int a = 0; a < data.length; a++)
{
System.out.print(data[a]+" ");
}
}
}
|
6 堆排序
6.1 说明
n个关键字序列K1,K2,K3…Kn称为(Heap),当且仅当序列满足如下性质时(堆性质):
ki<=k(2i)且ki<=k(2i+1)(1<=i<=n/2),当然这是小根堆,大根堆则换成>=;
若将此序列所存储的向量R[1..n]看做是一颗完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树。
对于小根堆:
树中任一非叶子节点的关键字均不大于其左右孩子,即根节点为最小值;
对于大根堆:
树中任一非叶子节点的关键字均大于其左右孩子,即根节点是最大值。
通过上面的介绍我们发现,对于一个数组进行堆排序的关键就是将其建立为一个堆。只要建立成堆后,就选择出了最大值和最小值。
6.2 堆排序的过程
对于包含n个元素的数组
n 第一趟将索引0-n-1处的全部数据建成大根或小根堆,就可以选择出最大值或最小值;
n 将上一步建立的堆的根节点与这组数据的最后一个节点交换,就使得其最大值或最小值排在最后。
n 第2趟将索引0-n-2处的全部数据建成大根堆或小根堆,就可以选择出这组数据中的最大值或最小值
n 将上一步建立的堆的根节点与这组数据的倒数第2个节点交换,就使得其最大值或最小值在倒数第2个;
n 第K趟将索引0-n-k处的全部数据建成大根堆或小根堆,就可以选择出这组数据中最大值或最小值;
n 将上一步建立的堆的根节点与这组数据的倒数第k个节点交换,就使得其最大值或最小值排在倒数第k个。
堆排序就是不断的重复一下两步:
建堆 拿堆的根节点与最后一个元素比较或交换
不难发现对于n个数据元素的数组,堆排序需要经过n-1次建堆。每次建堆的作用就是选出最值,因为堆排序的本身就是一种选择排序。
堆排序与直接选择排序的差别在于,堆排序可通过树形结构及时的保存部分比较结果,可减少比较次数而提升效率。
6.3 算法
建堆的过程
(1) 先把数组转换成完成二叉树
(2) 从最后一个非叶子节点开始,比较它于两个子节点的值。如果某个子节点的值大于父节点的值,就交换两者。
(3) 向前逐步提升至根节点,即保证每个父节点的值都大于或等于两个子节点,建堆完成。
6.4 代码示例
/*
* fileName:HeapSort.java
*
* Description: 堆排序
* author:@xflig
* Version: @version1.0
* Date: 2017-9-22 下午5:01:22
*
*/
public class HeapSort
{
public static void main( String[] args )
{
int[] data = {49,38,65,97,76,13,27,14,10};
for(int i = data.length-1; i>0;i--)
{
//建堆
buildMaxHead(data,i);
//交换根节点和最后一个节点
swap( data, 0, i );
//打印每排序结果
System.out.print("第"+(data.length-i)+"趟堆排序的结果是:");
for(int a = 0; a < data.length; a++ )
{
System.out.print(data[a] +" ");
}
System.out.println();
}
//打印最终排序结果
System.out.print("最终堆排序的结果是:");
for(int a = 0; a < data.length; a++ )
{
System.out.print(data[a] +" ");
}
}
/**
*
* Description:从0-lastIndex建堆
*
* @param data
* @param lastIndex
* @see
*/
public static void buildMaxHead(int[] data ,int lastIndex)
{
//获取最后的父节点
int lastParentIndex = (lastIndex-1)/2;
for(int i = lastParentIndex;i >= 0; i--)
{
int parent = data[i];
//左节点肯定存在
int leftChild = data[i*2+1];
int rightChild = leftChild;
if(i*2+2 <= lastIndex)
{
rightChild = data[i*2+2];
}
int maxIndex = leftChild < rightChild ? i*2+2:i*2+1;
if(parent < data[maxIndex])
{
swap( data, i, maxIndex );
}
}
}
/**
*
* Description:交换元素位置
*
* @param data
* @param first
* @param second
* @see
*/
private static void swap(int[] data,int first,int second)
{
int temp = data[first];
data[first] = data[second];
data[second] = temp;
}
}
|
6.5 总结
堆排序的关键在于建堆,这样就可以选择其中的最大元素值。然后放置在最后一个位置上。假设有n个数据,需要进行n-1次建堆。每次建堆消耗的时间是以2为底的n的对数。时间效率为O(lnN).堆排序的空间效率很高O(1)。所以是不稳定的。
7 直接插入排序
插入排序是一种非常常见的排序方法,包含直接插入,shell排序和折半插入。
7.1 算法
每次从无需表中取出第一个元素,把他插入到有序表的核实位置,使有序表仍然有效。
第一趟比较前两个数,然后把第二个数按大小插入到有序表中;
第二趟把第三个数与前两个数从前向后扫描,把第三个数按大小插入到有序表中;
依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。
7.2 代码示例
/*
* fileName:DirectionInsertion.java
*
* Description: 直接选择排序
* author:@xflig
* Version: @version1.0
* Date: 2017-9-24 上午10:23:55
*
*/
public class DirectionInsertion
{
public static void main( String[] args )
{
int[] data = {37,38,66,97,76,13,27,49,78,34,12,64,5,4,62,99
,98,54,56,18,17,23,34,15,3525,53,51};
for(int i = 1;i<data.length;i++)
{
int temp = data[i];
//如果索引为i比i-1小,则把i插到i-1处
if(temp < data[i-1])
{
int j = i-1;
for(;j>=0&&data[j]>temp;j--)
{
data[j+1] = data[j];
}
//最后把i放在合适的位置
data[j+1] = temp;
}
}
for(int a = 0;a < data.length;a++)
{
System.out.print(data[a]+" ");
}
}
}
|
直接插入排序的空间效率很高O(1).时间复杂度O(n的平方),直接插入排序是文鼎的。
8 折半插入排序
8.1 介绍
折半插入排序(binary insertion sort)是针对直接插入排序算法的一种改进,对于直接插入排序算法而言。当第i-1趟需要将第i个元素插入到前面的0-i-1个元素序列时,它总是从i-1个元素开始,直到找到它的位置。这显然没有利用前面的i-1已经是有序的特点,而折半查找排序则是改进了这一点。
8.2 算法
对于折半插入排序,在第i-1趟需要把第i个元素插入到前面的0-i-1元素序列时,他不会直接一次比较。具体做法是:
(1) 计算0-i-1索引的中间点,也就是用i索引和(0+i-1)/2处的元素进行比较,如果i索引处的元素大,则(0+i-1)/2~i-1半个范围内搜索,反之在0~(0+i-1)/2半个范围内搜索,这就是所谓的折半。
(2) 在确定好的半个范围之内不断的重复第一步的操作,范围不断缩小。1/2,1/4,1/8从而确定第i个元素的插入位置;
(3) 之后的做法和前面的大致一样。
8.3 代码示例
/*
* fileName:BinaryInsertion.java
*
* Description: 折半插入算法
* author:@xflig
* Version: @version1.0
* Date: 2017-9-27 下午4:14:31
*
*/
public class BinaryInsertion
{
public static void main( String[] args )
{
int[] data = {37,38,66,97,76,13,27,49,78,34,12,64,5,4,62,99
,98,54,56,18,17,23,34,15,3525,53,51};
//开始执行折半插入排序
binaryInsertion(data);
//打印排序后的结果
System.out.print("最终的排序结果是:");
for(int a = 0; a < data.length; a++)
{
System.out.print(data[a]+" ");
}
}
/**
*
* Description:折半插入排序
*
* @param data
* @see
*/
public static void binaryInsertion(int[] data)
{
for(int i = 1; i < data.length; i++)
{
int temp = data[i];
int low = 0;
int high = i-1;
while(low <= high)
{
//中间序号
int middle = (low + high) / 2;
if(temp > data[middle])
{
low = middle + 1;
}
else {
high = middle -1;
}
}
//将low到i之间的元素整体向后移动1位
for(int j =i ;j > low; j--)
{
data[j] = data[j-1];
}
//将temp放到合适的值
data[low] = temp;
System.out.print("第" + (i+1)+"趟排序的结果是:");
for(int a = 0 ; a < data.length; a++)
{
System.out.print(data[a]+" ");
}
System.out.println();
}
}
}
|
9 希尔排序
9.1 介绍
对于直接插入排序而言,当插入排序进行到一半时,待插值左边的所有的数据都已经是有序状态,直接插入和折半插入都把待插值存储到衣蛾临时变量里。然后,从待插值左边第一个数据单元开始,只要该数据单元的值大于待插值,就把该数据单元向右移动一格。直到找到第一个带插值的数据单元。接下来,将临时变量里的值放入小于待插值的数据单元之后(前面所有的数据都右移过,因此该数据单元有一个空格)。
分析上面的算法,我们发现一个问题:如果我们有一个很小的元素位于最右端,则在排序过程中中间所有的数据都有向右移动一格。而shell排序对其做了改进,通过增大插入排序之间的间隔,而使得这些数据跨度的移动,当这些数据项排过一次序之后,shell排序算法减少数据项的间隔再进行排序,一次下去。进行这些排序的数据项之间的间隔称为增量。
9.2 算法思想
待排序列有n个元素,先取一个小于n的整数h1作为第一个增量,把待排序序列以间隔h1分成若干子序列,子序列内使用插入排序;然后取第二个增量h2(<h1),重复上面的划分和排序,直至所取的增量hl=1(h1>h2>….>hl).
这样不管序列多么庞大,在先前较大步长分组下的每个子序列规模不是很大,用直接插入效率很高;后面步长变小,子序列变大,但由于整体有序性越来越明显,排序效率依赖很高,大大提高了时间效率。
10 归并排序
10.1 介绍
将两个有序数列合并为一个有序序列,我们称之为“归并”。
归并排序(Merge Sort)就是利用归并思想岁数列进行排序。根据具体的实现,归并排序包括:“从上到下”、“从下往上”2种方式。
10.2 “从下往上”的归并排序
将排序的数列分为若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将他们两两合并;直至合并成一个数列。这样就得到我们想要的结果。
10.3 “从上往下”的归并排序
(1) 分解—将当前区间一分为二,即求分裂点mid = (low + high)/2;
(2) 求解—递归地对两个子区间a[low..mid]和a[mid+1…high]进行归并排序。递归的终结条件就是子区间的长度为1;
(3) 合并—将已排好序的两个子区间a[low..mid]和a[mid+1…high]归并为一个有序的区间a[low…high].
10.4 代码示例
/*
* fileName:MergeSort.java
*
* Description: 归并排序
* author:@xflig
* Version: @version1.0
* Date: 2017-9-28 下午2:30:00
*
*/
public class MergeSort
{
public static void main( String[] args )
{
int[] data = {37,38,66,97,76,13,27,49,78,34,12,64,5,4,62,99
,98,54,56,18,17,23,34,15,3525,53,51};
sort(data,0,data.length-1);
//打印最终的排序结果
System.out.print("最终的排序结果是:");
for(int a=0; a < data.length;a++ )
{
System.out.print(data[a]+" ");
}
}
//拆分数组
public static void sort(int data[],int left,int right)
{
if(left >= right)
{
return;
}
//获取中间值
int center = (left + right)/2;
//对最侧数组进行拆分
sort( data, left, center );
//对右侧数组进行拆分
sort( data, center+1, right );
//归并排序
merge( data, left, center, right );
}
/**
*
* Description:对两个数组进行归并排序
*
* @param data 数组对象
* @param left 第一个数组的第一个元素
* @param center 第一个数组的最后一个元素
* @param right 第二个数组的最后一个元素
* @see
*/
public static void merge(int[] data,int left,int center,int right)
{
//创建临时存放数组
int[] tempArr = new int[data.length];
//记录临时数组的元素索引
int third = left;
//右边数组的第一个元素
int mid = center + 1;
//缓存左边数组元素的索引
int tmp = left;
while(left <= center && mid <= right)
{
if(data[left] <= data[mid])
{
tempArr[third++] = data[left++];
}
else {
tempArr[third++] = data[mid++];
}
}
while(mid <= right)
{
tempArr[third++] = data[mid++];
}
while(left <= center)
{
tempArr[third++] = data[left++];
}
while(tmp<=right)
{
data[tmp] = tempArr[tmp++];
}
}
}
|
11 桶排序
11.1 介绍
通排序(Bucket sort)或所谓的箱排序,是一个排序算法。工作原理是将矩形分到有限数量的箱子里。每个箱子再个别排序(有可能使用别的排序算法或是以递归方式继续使用通排序进行排序)。通排序是鸽巢排序的一种归纳结果。当要被排序的阵列内的数值是均匀分配的时候,通排序使用线性时间(O(n))。但桶排序并不是比较排序,它不受o(nlogn)下限的影响。
桶式排序不在是一种比较的排序算法,它是一种比较巧妙的排序方式,但这种排序方式需要待排序的序列满足一下两个特征:
(1) 待排序列所有的值处于一个可枚举的范围之类;
(2) 待排序所在的这个可枚举的范围不应该太大,否则排序开销太大;
11.2 代码示例
/*
* fileName:BucketSort.java
* Owner:Copyright by www.xflig.com
*
* Desc:
* Author:@xflig
* Date:2017-9-29 上午10:47:56
*
*/
package com.xflig.bucket_sort;
/*
* fileName:BucketSort.java
*
* Description:
* author:@xflig
* Version: @version1.0
* Date: 2017-9-29 上午10:47:56
*
*/
public class BucketSort
{
public static void main( String[] args )
{
int[] data = {37,38,66,97,76,13,27,49,78,34,12,64,5,4,62,99
,98,54,56,18,17,23,34,15,3525,53,51};
//通排序
//知道最大数
int max = getMaxValue(data);
bucketSort(data,max+1);
System.out.print("最终的排序结果是:");
for(int i = 0 ;i < data.length;i++)
{
System.out.print(data[i] + " ");
}
}
public static int getMaxValue(int[] data)
{
int max = data[0];
for(int i=1;i<data.length;i++){
if(data[i]>max){
max=data[i];
}
}
return max;
}
/**
*
* Description:桶排序
*
* @param data
* @param max
* @see
*/
public static void bucketSort(int[] data ,int max)
{
int[] temp ;
if(null == data || max< 1)
{
return ;
}
temp = new int[max];
//开始计数
for(int a = 0; a < data.length;a++)
{
temp[data[a]]++;
}
//排序
for(int i = 0 ,j = 0; i < max;i++ )
{
while((temp[i]--)>0)
{
data[j++] = i ;
}
}
//释放资源
temp = null;
}
}
|
12 基数排序
12.1 介绍
基数排序(Radix Sort)是桶排序的扩展,他的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
具体做法:将所有带比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成了有个有序数列。
12.2 图文说明
在上图中,首先将所有待比较的数统一为同一位数长度,接着从最低位开始,依次进行排序。
1. 按照个位数进行排序;
2. 按照十位数进行排序;
3. 按照百位数进行排序。
排序后,数列就变成了一个有序序列。
12.3 代码示例
/*
* fileName:RadixSort.java
*
* Description: 基数排序
* author:@xflig
* Version: @version1.0
* Date: 2017-9-29 下午3:05:10
*
*/
public class RadixSort
{
public static void main( String[] args )
{
int[] data = {37,38,66,97,76,13,27,49,78,34,12,64,5,4,62,99
,98,54,56,18,17,23,34,15,3525,53,51};
sort(data);
for(int i=0; i < data.length; i++)
{
System.out.print(data[i]+" ");
}
}
public static void sort(int[] data)
{
int max = getMax(data);
int time = 0;
//判断位数
while(max > 10)
{
max = max / 10;
time++;
}
//建立10个队列
List<ArrayList> queue = new ArrayList<ArrayList>();
for(int i=0;i<10;i++)
{
ArrayList<Integer> queue1 = new ArrayList<Integer>();
queue.add( queue1 );
}
//进行time次分配和收集
for(int i = 0; i < time ; i++)
{
//分配数组元素
for(int j=0; j< data.length;j++)
{
//得到数字的第time+1位数
int x = data[j]%(int)Math.pow( 10, i+1 )/(int)Math.pow( 10, i );
ArrayList<Integer> queue2 = queue.get( x );
queue2.add( data[j] );
queue.set( x, queue2 );
}
int count = 0;//元素计数器
for(int k = 0; k<10;k++)
{
while(queue.get( k ).size() >0)
{
ArrayList<Integer> queue3 = queue.get( k );
data[count] = queue3.get( 0 );
queue3.remove( 0 );
count++;
}
}
}
}
/**
*
* Description:求最大值
*
* @param data
* @return
* @see
*/
public static int getMax(int[] data)
{
int max = data[0];
for(int a = 0; a< data.length;a++)
{
if(data[a] > max)
{
max = data[a] ;
}
}
return max;
}
|