排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。
算法一:插入排序
步骤
- 假设待排序的记录存放在数组R[1..n]中。
- 初始时,R[1]自成1个有序区,无序区为R[2..n]。
- 从i=2起直至i=n为止,依次将R[i]插入当前的有序区R[1..i-1]中, 生成含n个记录的有序区。
时间复杂度
直接插入,平均情况O(n2),最好情况O(n),最坏情况O(n2),辅助空间O(1)。
稳定性
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
C
#include <stdio.h>
#include <stdlib.h>
#define N 10
void InsertSort(int a[], int size)
{
int i, j, k, temp = 0;
for(i = 1; i < size; i++)
{
temp = a[i];
for(j = 0; j < i; j++)
{
if(temp < a[j])
{
for(k = i - 1; k >= j; k--)
{
a[k + 1] = a[k];
}
a[j] = temp;
break;
}
}
}
}
int main()
{
int m = 0;
int b[N] = {9, 3, 7, 6, 8, 4, 5, 2, 1};
printf("\n排序前的数据是:\n9 3 7 6 8 4 5 2 1\n");
InsertSort(b, N);
printf("排序后的结果是:\n");
for(m = 0; m < N; m++)
{
printf(" %d ", b[m]);
}
printf("\n");
return 0;
}
JAVA
package com.company;
public class Main {
public static void InsertSort(int a[])
{
int i,j,k;
int size=a.length;
for(i = 1; i < size; i++)
{
int temp=a[i];
for(j = 0; j < i; j++)
{
if(temp < a[j])
{
for(k = i - 1; k >= j; k--)
{
a[k + 1] = a[k];
}
a[j] = temp;
break;
}
}
}
}
public static void main(String[] args) {
int m = 0;
int[] b = {9, 3, 7, 6, 8, 4, 5, 2, 1};
System.out.print("\n排序前的数据是:\n9 3 7 6 8 4 5 2 1\n");
InsertSort(b);
System.out.print("排序后的结果是:\n");
for(m = 0; m < 10; m++)
{
System.out.print( b[m]);
}
System.out.println();
}
}
算法二 希尔排序
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序属于插入类排序,是将整个有序序列分割成若干小的子序列分别进行插入排序。
排序过程:
先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;
然后取d2<d1,重复上述分组和排序操作;
直至di=1,即所有记录放进一个组中排序为止。
稳定性
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
Shell排序的算法实现:
不设监视哨的算法描述
void ShellPass(SeqList R,int d)
{//希尔排序中的一趟排序,d为当前增量
for(i=d+1;i<=n;i++) //将R[d+1..n]分别插入各组当前的有序区
if(R[ i ].key<R[i-d].key){
R[0]=R[i];j=i-d; //R[0]只是暂存单元,不是哨兵
do {//查找R的插入位置
R[j+d]=R[j]; //后移记录
j=j-d; //查找前一记录
}while(j>0&&R[0].key<R[j].key);
R[j+d]=R[0]; //插入R到正确的位置上
} //endif
C
#include<stdio.h>
#include<math.h>
#define MAXNUM 10
void main()
{
void shellSort(int array[],int n,int t);//t为排序趟数
int array[MAXNUM],i;
for(i=0;i<MAXNUM;i++)
scanf("%d",&array[i]);
shellSort(array,MAXNUM,(int)(log(MAXNUM+1)/log(2)));//排序趟数应为log2(n+1)的整数部分
for(i=0;i<MAXNUM;i++)
printf("%d ",array[i]);
printf("\n");
}
//根据当前增量进行插入排序
void shellInsert(int array[],int n,int dk)
{
int i,j,temp;
for(i=dk;i<n;i++)//分别向每组的有序区域插入
{
temp=array[i];
for(j=i-dk;(j>=i%dk)&&array[j]>temp;j-=dk)//比较与记录后移同时进行
array[j+dk]=array[j];
if(j!=i-dk)
array[j+dk]=temp;//插入
}
}
//计算Hibbard增量
int dkHibbard(int t,int k)
{
return (int)(pow(2,t-k+1)-1);
}
//希尔排序
void shellSort(int array[],int n,int t)
{
void shellInsert(int array[],int n,int dk);
int i;
for(i=1;i<=t;i++)
shellInsert(array,n,dkHibbard(t,i));
}
JAVA
public static void main(String [] args)
{
int[]a={49,38,65,97,76,13,27,49,78,34,12,64,1};
System.out.println("排序之前:");
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+" ");
}
//希尔排序
int d=a.length;
while(true)
{
d=d/2;
for(int x=0;x<d;x++)
{
for(int i=x+d;i<a.length;i=i+d)
{
int temp=a[i];
int j;
for(j=i-d;j>=0&&a[j]>temp;j=j-d)
{
a[j+d]=a[j];
}
a[j+d]=temp;
}
}
if(d==1)
{
break;
}
}
System.out.println();
System.out.println("排序之后:");
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+" ");
}
}
算法三 简单选择排序
简单选择排序
- 设所排序序列的记录个数为n。
- i取1,2,…,n-1,从所有n-i+1个记录(Ri,Ri+1,…,Rn)中找出排序码最小的记录,与第i个记录交换。
- 执行n-1趟 后就完成了记录序列的排序。
- 生成含n个记录的有序区。
时间复杂度
在简单选择排序过程中,所需移动记录的次数比较少。最好情况下,即待排序记录初始状态就已经是正序排列了,则不需要移动记录。最坏情况下,即待排序记录初始状态是按第一条记录最小,之后的记录从小到大顺序排列,则需要移动记录的次数最多为3(n-1)。简单选择排序过程中需要进行的比较次数与初始状态下待排序的记录序列的排列情况无关。当i=1时,需进行n-1次比较;当i=2时,需进行n-2次比较;依次类推,共需要进行的比较次数是(n-1)+(n-2)+…+2+1=n(n-1)/2,即进行比较操作的时间复杂度为O(n^2),进行移动操作的时间复杂度为O(n)。
稳定性
简单选择排序是不稳定排序。
C
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i,j,t;
int temp=0;
int a[10]={3,2,5,8,4,7,6,9,1,0};
printf("\n排序前的数据是:\n3 2 5 8 4 7 6 9 1 0\n");
for(i=0;i<10;i++)
{
t=i;
for(j=i+1;j<10;j++)
{
if(a[t]>a[j])
t=j;
}
temp=a[i];
a[i]=a[t];
a[t]=temp;
}
printf("排序后数据是:\n");
for(i=0;i<10;i++)
printf("%3d",a[i]);
}
JAVA
package com.company;
public class SimpleSort {
public static void main(String[] args) {
int i,j,t;
int temp=0;
int[] a={3,2,5,8,4,7,6,9,1,0};
int size=a.length;
System.out.print("\n排序前的数据是:\n3 2 5 8 4 7 6 9 1 0\n");
for(i=0;i<size;i++)
{
t=i;
for(j=i+1;j<size;j++)
{
if(a[t]>a[j])
t=j;
}
temp=a[i];
a[i]=a[t];
a[t]=temp;
}
System.out.print("排序后数据是:\n");
for(i=0;i<10;i++)
System.out.print(a[i]+"\t");
}
}
算法四 堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
C
#include<stdio.h>
int c=0;
/*heapadjust()函数的功能是实现从a[m]到a[n]的数据进行调整,使其满足大顶堆的特性*/
/*a[]是待处理的数组,m是起始坐标, n是终止坐标*/
void heapadjust(int a[], int m, int n)
{
int i, temp;
temp=a[m];
for(i=2*m;i<=n;i*=2)//从m的左孩子开始
{
if(i+1<=n && a[i]<a[i+1])//如果左孩子小于右孩子,则将i++,这样i的值就是最大孩子的下标值
{
i++;
}
if(a[i]<temp)//如果最大的孩子小于temp,则不做任何操作,退出循环;否则交换a[m]和a[i]的值,将最大值放到a[i]处
{
break;
}
a[m]=a[i];
m=i;
}
a[m]=temp;
}
void crtheap(int a[], int n)//初始化创建一个大顶堆
{
int i;
for(i=n/2; i>0; i--)//n/2为最后一个双亲节点,依次向前建立大顶堆
{
heapadjust(a, i, n);
}
}
/*swap()函数的作用是将a[i]和a[j]互换*/
void swap(int a[], int i, int j)
{
int temp;
temp=a[i];
a[i]=a[j];
a[j]=temp;
c++;
}
void heapsort(int a[], int n)
{
int i;
crtheap(a, n);
for(i=n; i>1; i--)
{
swap(a, 1, i);//将第一个数,也就是从a[1]到a[i]中的最大的数,放到a[i]的位置
heapadjust(a, 1, i-1);//对剩下的a[1]到a[i-1],再次进行堆排序,选出最大的值,放到a[1]的位置
}
}
int main(void)
{
int i;
int a[10]={-1,5,2,6,0,3,9,1,7,4};
printf("排序前:");
for(i=1;i<10;i++)
{
printf("%d",a[i]);
}
heapsort(a, 9);
printf("\n\n共交换数据%d次\n\n", c);
printf("排序后:");
for(i=1;i<10;i++)
{
printf("%d",a[i]);
}
printf("\n\n\n");
return 0;
}
JAVA
package sortdemo;
import java.util.Arrays;
/**
- Created by chengxiao on 2016/12/17.
- 堆排序demo
*/
public class HeapSort {
public static void main(String []args){
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int []arr){
//1.构建大顶堆
for(int i=arr.length/2-1;i>=0;i--){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr,0,j);//重新对堆进行调整
}
}
/**
* 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
* @param arr
* @param i
* @param length
*/
public static void adjustHeap(int []arr,int i,int length){
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
/**
* 交换元素
* @param arr
* @param a
* @param b
*/
public static void swap(int []arr,int a ,int b){
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
算法五 冒泡排序
冒泡排序算法的原理如下:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
时间复杂度
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:C(min)=n-1,M(min)=0。所以,冒泡排序最好的时间复杂度为O(n)。若初始文件是反序的,需要进行n-1趟排序。每趟排序要进行n-i次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:
C(max)=n(n-1)/2=O(n^2),M(max)=3n(n-1)/2=O(n^2)。冒泡排序的最坏时间复杂度为O(n^2)
综上,因此冒泡排序总的平均时间复杂度为 O(n^2)
稳定性
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。
C
#include <stdio.h>
#define N 10
void InsertSort(int a[], int n)
{
int i, j, temp;
for (j=0;j<n-1;j++)
for (i=0;i<n-1-j;i++)
{
if(a[i]>a[i+1])
{
temp=a[i];
a[i]=a[i+1];
a[i+1]=temp;
}
}
}
int main()
{
int a[N] = {3,2,5,8,4,7,6,9,1,0};
printf("\n排序前的数据是:\n3 2 5 8 4 7 6 9 1 0\n");
int i;
InsertSort(a,N);
printf("排序后的数据是:\n");
for(i=0;i <N;i++)
{
printf("%3d", a[i]);
}
}
JAVA
package com.company;
public class InsertSort {
public static void main(String[] args) {
int[] a = {3, 2, 5, 8, 4, 7, 6, 9, 1, 0};
System.out.print("\n排序前的数据是:\n3 2 5 8 4 7 6 9 1 0\n");
int size = a.length;
int i;
for (i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {//-1为了防止溢出
if (a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
System.out.print("排序后数据是:\n");
for(i=0;i<10;i++)
System.out.print(a[i]+"\t");
}
}
算法六 归并排序
归并操作的工作原理如下:
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
C
#include <stdlib.h>
#include <stdio.h>
void Merge(int a[],int b[], int start, int mid, int end)
{
int i=start,j=mid+1,k=start;
while(i!=mid+1&&j!=end+1)
{
if(a[i]>a[j])
b[k++]=a[j++];
else
b[k++]=a[i++];
}
while(i!=mid+1)
b[k++]=a[i++];
while(j!=end+1)
b[k++]=a[j++];
for(i=start;i<=end;i++)
a[i]=b[i];
}
//内部使用递归
void MergeSort(int a[], int b[], int start, int end)
{
int mid;
if(start<end)
{
mid=start+(end-start)/2;//避免溢出int
MergeSort(a,b,start,mid);
MergeSort(a,b,mid+1,end);
Merge(a,b,start,mid,end);
}
}
int main()
{
int a[10] = {3,2,5,8,4,7,6,9,1,0};
printf("\n排序前的数据是:\n3 2 5 8 4 7 6 9 1 0\n");
int i, b[10];
MergeSort(a, b, 0, 9);
printf("排序后的数据是:\n");
for(i=0;i<10;i++)
printf("%3d",a[i]);
printf("\n");
return 0;
}
JAVA
package com.company;
public class MergeSort {
// private static long sum = 0;
/**
* * <pre>
* * 二路归并
* * 原理:将两个有序表合并和一个有序表
* * </pre>
* *
* * @param a
* * @param s
* * 第一个有序表的起始下标
* * @param m
* * 第二个有序表的起始下标
* * @param t
* * 第二个有序表的结束下标
* *
*/
private static void merge(int[] a, int s, int m, int t) {
int[] tmp = new int[t - s + 1];
int i = s, j = m, k = 0;
while (i < m && j <= t) {
if (a[i] <= a[j]) {
tmp[k] = a[i];
k++;
i++;
} else {
tmp[k] = a[j];
j++;
k++;
}
}
while (i < m) {
tmp[k] = a[i];
i++;
k++;
}
while (j <= t) {
tmp[k] = a[j];
j++;
k++;
}
System.arraycopy(tmp, 0, a, s, tmp.length);
}
/**
* *
* * @param a
* * @param s
* * @param len
* * 每次归并的有序集合的长度
*/
public static void mergeSort(int[] a, int s, int len) {
int size = a.length;
int mid = size / (len << 1);
int c = size & ((len << 1) - 1);
// -------归并到只剩一个有序集合的时候结束算法-------//
if (mid == 0)
return;
// ------进行一趟归并排序-------//
for (int i = 0; i < mid; ++i) {
s = i * 2 * len;
merge(a, s, s + len, (len << 1) + s - 1);
}
// -------将剩下的数和倒数一个有序集合归并-------//
if (c != 0)
merge(a, size - c - 2 * len, size - c, size - 1);
// -------递归执行下一趟归并排序------//
mergeSort(a, 0, 2 * len);
}
public static void main(String[] args) {
int[] a = new int[]{3, 2, 5, 8, 4, 7,6,9,1,0};
System.out.print("\n排序前的数据是:\n3 2 5 8 4 7 6 9 1 0\n");
mergeSort(a, 0, 1);
System.out.print("排序后数据是:\n");
for (int i = 0; i < a.length; ++i) {
System.out.print(a[i] + " ");
}
}
}
算法七 快速排序
一趟快速排序的算法是:
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]互换;
重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
C
void sort(int *a, int left, int right)
{
if(left >= right)/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
{
return ;
}
int i = left;
int j = right;
int key = a[left];
while(i < j) /*控制在当组内寻找一遍*/
{
while(i < j && key <= a[j])
/*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升
序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/
{
j--;/*向前寻找*/
}
a[i] = a[j];
/*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是
a[left],那么就是给key)*/
while(i < j && key >= a[i])
/*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,
因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
{
i++;
}
a[j] = a[i];
}
a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
sort(a, left, j - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
sort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/
/*当然最后可能会出现很多分左右,直到每一组的i = j 为止*/
}
JAVA
class Quick
{
public void sort(int arr[],int low,int high)
{
int l=low;
int h=high;
int povit=arr[low];
while(l<h)
{
while(l<h&&arr[h]>=povit)
h--;
if(l<h){
int temp=arr[h];
arr[h]=arr[l];
arr[l]=temp;
l++;
}
while(l<h&&arr[l]<=povit)
l++;
if(l<h){
int temp=arr[h];
arr[h]=arr[l];
arr[l]=temp;
h--;
}
}
print(arr);
System.out.print("l="+(l+1)+"h="+(h+1)+"povit="+povit+"\n");
if(l>low)sort(arr,low,l-1);
if(h<high)sort(arr,l+1,high);
}
}
/*//////////方式二////////////*/
更高效点的代码:
public<TextendsComparable<?superT>>
T[]quickSort(T[]targetArr,intstart,intend)
{
inti=start+1,j=end;
Tkey=targetArr[start];
SortUtil<T>sUtil=newSortUtil<T>();
if(start>=end)return(targetArr);
/*从i++和j--两个方向搜索不满足条件的值并交换
*
*条件为:i++方向小于key,j--方向大于key
*/
while(true)
{
while(targetArr[j].compareTo(key)>0)j--;
while(targetArr[i].compareTo(key)<0&&i<j)i++;
if(i>=j)break;
sUtil.swap(targetArr,i,j);
if(targetArr[i]==key)
{
j--;
}else{
i++;
}
}
/*关键数据放到‘中间’*/
sUtil.swap(targetArr,start,j);
if(start<i-1)
{
this.quickSort(targetArr,start,i-1);
}
if(j+1<end)
{
this.quickSort(targetArr,j+1,end);
}
returntargetArr;
}
/*//////方式三:减少交换次数,提高效率/////////*/
private<TextendsComparable<?superT>>
voidquickSort(T[]targetArr,intstart,intend)
{
inti=start,j=end;
Tkey=targetArr[start];
while(i<j)
{
/*按j--方向遍历目标数组,直到比key小的值为止*/
while(j>i&&targetArr[j].compareTo(key)>=0)
{
j--;
}
if(i<j)
{
/*targetArr[i]已经保存在key中,可将后面的数填入*/
targetArr[i]=targetArr[j];
i++;
}
/*按i++方向遍历目标数组,直到比key大的值为止*/
while(i<j&&targetArr[i].compareTo(key)<=0)
/*此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。*/
{
i++;
}
if(i<j)
{
/*targetArr[j]已保存在targetArr[i]中,可将前面的值填入*/
targetArr[j]=targetArr[i];
j--;
}
}
/*此时i==j*/
targetArr[i]=key;
/*递归调用,把key前面的完成排序*/
this.quickSort(targetArr,start,i-1);
/*递归调用,把key后面的完成排序*/
this.quickSort(targetArr,j+1,end);
}
算法八 基数排序
第一步
以LSD为例,假设原来有一串数值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
0,1 81,2 22,3 73 93 43,4 14,5 55 65,6,7,8 28,9 39
第二步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再进行一次分配,这次是根据十位数来分配:
0,1 14,2 22 28,3 39,4 43,5 55,6 65,7 73,8 81,9 93
第三步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。
LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好。MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中。
时间复杂度:O (nlog(r)m)
C
#include<stdio.h>
#include<math.h>
testBS()
{
inta[] = {2, 343, 342, 1, 123, 43, 4343, 433, 687, 654, 3};
int *a_p = a;
//计算数组长度
intsize = sizeof(a) / sizeof(int);
//基数排序
bucketSort3(a_p, size);
//打印排序后结果
inti;
for(i = 0; i < size; i++)
{
printf("%d\n", a[i]);
}
intt;
scanf("%d", t);
}
//基数排序
voidbucketSort3(int *p, intn)
{
//获取数组中的最大数
intmaxNum = findMaxNum(p, n);
//获取最大数的位数,次数也是再分配的次数。
intloopTimes = getLoopTimes(maxNum);
inti;
//对每一位进行桶分配
for(i = 1; i <= loopTimes; i++)
{
sort2(p, n, i);
}
}
//获取数字的位数
intgetLoopTimes(intnum)
{
intcount = 1;
inttemp = num / 10;
while(temp != 0)
{
count++;
temp = temp / 10;
}
returncount;
}
//查询数组中的最大数
intfindMaxNum(int *p, intn)
{
inti;
intmax = 0;
for(i = 0; i < n; i++)
{
if(*(p + i) > max)
{
max = *(p + i);
}
}
returnmax;
}
//将数字分配到各自的桶中,然后按照桶的顺序输出排序结果
voidsort2(int *p, intn, intloop)
{
//建立一组桶此处的20是预设的根据实际数情况修改
intbuckets[10][20] = {};
//求桶的index的除数
//如798个位桶index=(798/1)%10=8
//十位桶index=(798/10)%10=9
//百位桶index=(798/100)%10=7
//tempNum为上式中的1、10、100
inttempNum = (int)pow(10, loop - 1);
inti, j;
for(i = 0; i < n; i++)
{
introw_index = (*(p + i) / tempNum) % 10;
for(j = 0; j < 20; j++)
{
if(buckets[row_index][j] == NULL)
{
buckets[row_index][j] = *(p + i);
break;
}
}
}
//将桶中的数,倒回到原有数组中
intk = 0;
for(i = 0; i < 10; i++)
{
for(j = 0; j < 20; j++)
{
if(buckets[i][j] != NULL)
{
*(p + k) = buckets[i][j];
buckets[i][j] = NULL;
k++;
}
}
}
}
JAVA
public class RadixSort
{
public static void sort(int[] number, int d) //d表示最大的数有多少位
{
intk = 0;
intn = 1;
intm = 1; //控制键值排序依据在哪一位
int[][]temp = newint[10][number.length]; //数组的第一维表示可能的余数0-9
int[]order = newint[10]; //数组orderp[i]用来表示该位是i的数的个数
while(m <= d)
{
for(inti = 0; i < number.length; i++)
{
intlsd = ((number[i] / n) % 10);
temp[lsd][order[lsd]] = number[i];
order[lsd]++;
}
for(inti = 0; i < 10; i++)
{
if(order[i] != 0)
for(intj = 0; j < order[i]; j++)
{
number[k] = temp[i][j];
k++;
}
order[i] = 0;
}
n *= 10;
k = 0;
m++;
}
}
public static void main(String[] args)
{
int[]data =
{73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100};
RadixSort.sort(data, 3);
for(inti = 0; i < data.length; i++)
{
System.out.print(data[i] + "");
}
}
}