最近重新看了一下排序算法,对一些经典排序算法做了代码实现,主要写了直接插入排序,希尔排序,冒泡排序,快速排序,简单选择排序,归并排序几种比较常用的排序算法,并分析了几种算法的排序效率。当排序数据量比较小的时候,几种排序算法消耗的时间都相差不大,但是当数据量较大时,直接插入排序和冒泡排序消耗的时间比快速排序和希尔排序算法消耗的时间要多得多,这时快排和希尔排序的优势就体现出来了,一比较,算法的效率对程序的鲁棒性是至关重要的,以下是分别对1万,10万,50万随机产生的数据用几种不同方法进行排序的时间比较。
排序数据为1万个时:
排序数据为10万个时:
排序数据为50万个时:
对各种排序算法进行时间复杂度分析:
以下是排序算法的java代码,每个算法原理都有说明,直接可以运行
/*
* 排序默认为递增
*/
public class InternalSort {
public final int ARRAY_LENGTH=100000;//定义数组长度
public final int MAX_LENGTH=10000000;//定义数组最大值
public int arraydata[];
public int quicksortArray[];
public InternalSort()
{
arraydata=new int[ARRAY_LENGTH];
//创建数组,传入参数false,创建给定数据,传入参数true,创建随机数组,数组长度都为ARRAY_LENGTH
creatArray(true);
quicksortArray=CopyArrayData();
}
/*
* 创建初始数组函数
*/
public void creatArray(boolean israndom)
{
if(israndom)
{
for(int i=0;i<ARRAY_LENGTH;i++)
{
int item=(int)(Math.random()*MAX_LENGTH);
arraydata[i]=item;
//System.out.print(item+",");
}
}else{
arraydata[0]=49;
arraydata[1]=38;
arraydata[2]=65;
arraydata[3]=97;
arraydata[4]=76;
arraydata[5]=13;
arraydata[6]=27;
arraydata[7]=49;
arraydata[8]=55;
arraydata[9]=4;
}
}
/*
* 直接插入排序
*/
public int[] StraightInsertionSort(int array[])
{
int temp;
for(int i=1;i<array.length;i++)//比较n-1次
{
temp=array[i];//将需要排序的一个记录保存下来
if(temp<array[i-1])
{
int index=0;
//如果插入的数据比前面的数要小,需将temp插入有序序列中
for(int j=i-1;ComparisonNumber(temp, array[j]);j--)
{
if(j==0)
{
//如果j=0,说明temp最小,直接插入到第一个位置,结束运行,防止j=-1发生索引越界
index=0;
array[j+1]=array[j];
break;
}
array[j+1]=array[j];
index=j;
}
array[index]=temp;//最后将记录插入
}
}
return array;
}
public void printArray(int[] array)
{
for(int i=0;i<array.length-1;i++)
{
System.out.print(array[i]+",");
}
System.out.println(array[array.length-1]);
}
public int[] getArraydata() {
return arraydata;
}
/*
* 复制数组
*/
public int [] CopyArrayData()
{
int array[]=new int[ARRAY_LENGTH];
for(int i=0;i<ARRAY_LENGTH;i++)
{
array[i]=arraydata[i];
}
return array;
}
/*
* 比较两个数的大小
*/
public boolean ComparisonNumber(int a,int b)
{
return a<b;
}
/*
* 对顺序表L作一趟希尔插入排序
*/
public int[] ShellInsert(int []L,int dk)
{
for(int i=0;i<dk;i++)
{
//间隔为dk的数据为一组,进行直接插入排序
for(int j=i;j<L.length-dk;j=j+dk)
{
int temp=L[j+dk];
if(ComparisonNumber(temp, L[j]))//如果后面的数比前面的数小
{
//System.out.println(temp);
int index=0;//记录排序数据应该插入的地址
for(int m=j+dk;(m-dk)>-1&&ComparisonNumber(temp, L[m-dk]);m=m-dk)
{
//防止数据下标为负数的情况
if(m-dk<0)
{
//如果m-dk小于0,则说明该数在最前面
index=m;
L[m+dk]=L[m];
break;
}
//System.out.println(L[m]+","+L[m-dk]);
L[m]=L[m-dk];
//System.out.println(L[m]+","+L[m-dk]);
index=m-dk;
}
L[index]=temp;
}
}
}
return L;
}
/*
* 希尔排序:先取一个小于n的整数d1作为第一个增量,
* 把文件的全部记录分成(n除以d1)个组。所有距离为d1的倍数的记录放在同一个组中。
* 先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,
* 直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
*/
public int[] ShellSort(int []L)
{
for(int n=L.length/2;n>0;n=n/2)
{
if(n==1)
{
/*
* 最后一趟希尔排序间隔必须是1,也就是最后一趟希尔排序是直接插入排序
*/
L=ShellInsert(L, 1);
return L;
}
L=ShellInsert(L, n);
}
return L;
}
/*
* 冒泡排序
*/
public int [] BubbleSort(int []L)
{
for(int i=0;i<L.length-1;i++)
for(int j=i+1;j<L.length;j++)
{
if(L[i]>L[j])
{
int temp=L[j];
L[j]=L[i];
L[i]=temp;
}
}
return L;
}
/*
* 快速排序:
* 一趟快速排序的算法是:
* 1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
* 2)以第一个数组元素作为关键数据,赋值给key,即 key=A[0];
* 3)从j开始向前搜索,即由后开始向前搜索(j=j-1即j--),
* 找到第一个小于key的值A[j],A[i]与A[j]交换;
* 4)从i开始向后搜索,即由前开始向后搜索(i=i+1即i++),
* 找到第一个大于key的A[i],A[i]与A[j]交换;
* 5)重复第3、4、5步,直到 I=J;
* (3,4步是在程序中没找到时候j=j-1,i=i+1,直至找到为止。
* 找到并交换的时候i, j指针位置不变。
* 另外当i=j这过程一定正好是i+或j-完成的最后令循环结束。)
*/
//一趟快速排序
public int Partion(int low,int high)
{
//printArray(quicksortArray);
int pivotkey=quicksortArray[low];//枢纽记录关键字
while(low<high)
{
//从最后向前寻找比pivotkey小的第一个关键字
while(low<high&&pivotkey<=quicksortArray[high])
high--;
quicksortArray[low]=quicksortArray[high];
//从前面向后寻找第一个比pivotkey大的第一个关键字
while(low<high&&pivotkey>=quicksortArray[low])
low++;
quicksortArray[high]=quicksortArray[low];
}
quicksortArray[low]=pivotkey;
return low;
}
/*
* 递归实现快速排序
*/
public void QuickSort(int low,int high)
{
if(low<high)
{
int position=Partion(low, high);//将数组一分为二
QuickSort(low, position-1);//对左边进行递归
QuickSort(position+1, high);//对右边进行递归
}
}
/*
* 选择排序,基本思想是:每一趟在n-i+1(i=1,2,3,...,n-1)个
* 记录中选择关键字最小的记录作为有序序列中第i个记录。其中最简单的
* 且为读者最熟悉的是简单选择排序,下面就对简单选择排序进行代码编写
*/
public int[] SimpleSelectionSort(int []L)
{
for(int i=0;i<L.length-1;i++)
{
//i开始的寻找最小关键字
int temp=L[i];
int position=i;
for(int j=i+1;j<L.length;j++)
{
if(temp>L[j])
{
temp=L[j];
position=j;
}
}
if(position!=i)
{
L[position]=L[i];
L[i]=temp;
}
}
return L;
}
/*
* 归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。
* 如设有数列{49,38,65,97,76,13,27}
* 初始状态: [49] [38] [65] [97] [76] [13] [27]
* i=1 [38 49 ] [ 65 97] [ 13 76] [ 27 ]
* i=2 [ 38 49 65 97 ] [ 13 27 76 ]
* i=3 [ 13 27 38 49 65 76 97 ]
*/
public int[] MergeSort(int[] nums, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
MergeSort(nums, low, mid);
// 右边
MergeSort(nums, mid + 1, high);
// 左右归并
merge(nums, low, mid, high);
}
return nums;
}
public 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[])
{
InternalSort mInternalSort=new InternalSort();
//直接插入排序
long start=System.currentTimeMillis();
int array[]=mInternalSort.StraightInsertionSort(mInternalSort.CopyArrayData());
System.out.print("直接插入排序:");
long end=System.currentTimeMillis();
//mInternalSort.printArray(array);
System.out.println("直接插入法排序"+mInternalSort.ARRAY_LENGTH+"个数消耗时间:"+(end-start)+"毫秒");
/*int shellsort1[]=mInternalSort.ShellInsert(mInternalSort.CopyArrayData(), 5);
System.out.print("一趟希尔排序:");
mInternalSort.printArray(shellsort1);
int shellsort2[]=mInternalSort.ShellInsert(shellsort, 3);
System.out.print("二趟希尔排序:");
mInternalSort.printArray(shellsort);*/
start=System.currentTimeMillis();
System.out.print("希尔排序:");
int shellsort[]=mInternalSort.ShellSort(mInternalSort.CopyArrayData());
end=System.currentTimeMillis();
//mInternalSort.printArray(shellsort);
System.out.println("希尔排序"+mInternalSort.ARRAY_LENGTH+"个数消耗时间:"+(end-start)+"毫秒");
//冒泡排序
start=System.currentTimeMillis();
System.out.print("冒泡排序:");
int bubblesort[]=mInternalSort.BubbleSort(mInternalSort.CopyArrayData());
end=System.currentTimeMillis();
//mInternalSort.printArray(bubblesort);
System.out.println("冒泡排序"+mInternalSort.ARRAY_LENGTH+"个数消耗时间:"+(end-start)+"毫秒");
//快速排序
start=System.currentTimeMillis();
System.out.print("快速排序:");
//System.out.print(mInternalSort.quicksortArray.length-1);
//int p=mInternalSort.Partion(0, mInternalSort.quicksortArray.length-1);
mInternalSort.QuickSort(0, mInternalSort.quicksortArray.length-1);
//mInternalSort.printArray(mInternalSort.quicksortArray);
end=System.currentTimeMillis();
System.out.println("快速排序"+mInternalSort.ARRAY_LENGTH+"个数消耗时间:"+(end-start)+"毫秒");
//简单选择排序
start=System.currentTimeMillis();
System.out.print("简单选择排序:");
int simpleselectionsort[]=mInternalSort.SimpleSelectionSort(mInternalSort.CopyArrayData());
//mInternalSort.printArray(simpleselectionsort);
end=System.currentTimeMillis();
System.out.println("简单选择排序"+mInternalSort.ARRAY_LENGTH+"个数消耗时间:"+(end-start)+"毫秒");
//归并排序
start=System.currentTimeMillis();
System.out.print("归并排序:");
int mergesort[]=mInternalSort.MergeSort(mInternalSort.CopyArrayData(), 0, mInternalSort.ARRAY_LENGTH-1);
//mInternalSort.printArray(mergesort);
end=System.currentTimeMillis();
System.out.println("归并排序"+mInternalSort.ARRAY_LENGTH+"个数消耗时间:"+(end-start)+"毫秒");
}
}