初级:1)冒泡排序
swap方法已在第一个代码中写好,等同三元组,在java中主要是引用思想。
public class BubbleSort{
public static void swap(int[]a,int x,int y){//三元组swap静态方法
int temp;
temp = a[x];
a[x] = a[y];
a[y] = temp;
}
public static void bubbleSort(int[] a){
for(int i = 0; i<a.length - 1;i++){//外循环排序次数
for(int j=0; j<a.length - i - 1;j++){//内循环进行交换
if(a[j]>a[j+1]){
swap(a,j,j+1);
}
}
}
}
}
2)选择排序
思想:比较所有的数据项,取出最小值,放左边。比较剩下的数据,取最小,放最左。。。。
内层循环中,每一个in的新位置,数据项a[in]和a[min]比较,如果a[in]更小,则min被赋值为in的值,这里只是下标,没交换。到一次内层循环结束,再交换数据项。这样,最小数据项就会一直被放在左边。
比较的次数和冒泡是一样的,但是交换的次数小。因为交换数据需要在内存中移动,时间上要多(java语言中,影响不大,只是改变引用位置而已)
public class ChooseSort{
public static void chooseSort(int []a,int nElems){
int i,j,min;
for(int i=0; i<nElems-1; i++){
i=min;
for(int j=i+1; j<nElems; j++){
if(a[j]<a[min]){
min = j;
}
swap(a,j,min)
}
}
}
}
3) 插入排序
思想:假设左边已经排序好了,从某位置(比如10)开始无序,将10赋予给一临时值,然后和前面的数据比较,如果9位置比10大,就9右移一位,继续和8比较。。。直到到数据的最右边或找到比10位置的数据排好了。在大多数情况下,插入算法仍然需要0(N^2)的时间,但比冒泡快一倍,比选择排序也还要快一点。经常被用在比较复杂的排序算法的最后阶段,例如快排。
public class InsertSort{
public static void insertSort(int []a,int nElem){
int in,out;
for(out = 0; out<nElem; out++){
long temp = a[out];
in = out;
while(in>0 && a[in-1] >=temp){
a[in] = a[in-1];
--in;
}
a[in] = temp;
}
}
}
外层的for循环中,out变量从1开始,向右移动,它标记了未排序部分的最左端的数据。
而在内层while循环中,in变量从out变量开始,向左移动,知道temp变量小于in所指的数组数据项,或者它已经不能再往左移动为止。while循环的每一次都向右移动了一个排序的数据项。
在每次结束时,在将temp位置的项插入后,比outer变量下标号小的数据项都是局部有序的。
比较次数,1+2+3+...+(N-1) = Nx(N-1)/2,而因为每一次排序发现插入点之前,平均只有全体数据项的一半真的进行了比较,所以是N*(N-1)/2 /2
复制的次数大致等于比较的次数。复制和交换的时间耗费不同。相对于随机数据,这个算法比冒泡快一倍,比选择排序略快。
对于已经有序或基本有序的数据来说,插入排序要好得多。对于逆序排列的数据,每次比较和移动都会执行,所以插入排序不比冒泡排序快。
中级:1)归并排序(时间复杂度为 O(NlogN))
归并排序的思想是:将一个数组分成两半,然后分别排序,然后将数组的两半合并,合并的时候需要比较大小(合并的时候还要考虑两小数组还有没有数据,即有可能一边有,另一边没有)。至于如何排序1/2半的数组,当然是再分成两个1/4数组,再排序。。。直到分割的小数组只有1个数据项,不用排序了。这是用到递归的思想
归并排序的缺点,需要在存储器中有另一个大小等于被排序的数据项数目的空间,用来复制分割出来的小数组。
归并算法的效率由来:需要复制log<sub>2</sub><sup>N</sup>层(分子数组),每一个层都是N个数据,所以是NxlogN.
int[] workSpace = new int[source.length];
recMergerSort(source,workSpace,0,length-1);
private static void reMergeSort(int[] source, int[] workSpace, int lowerBound, int upperBound){
if(lowerBound == upperBound){
return;
}else{
int mid = (lowerBound + upperBound) / 2;
recMergeSort(source, workSpacce, lowerBound, mid);
recMergeSort(source,workSpace,mid+1,upperBound);
}
}
高级:Shell排序
希尔排序基于插入排序,但增加了一个新的特性,大大地提高了插入排序的执行效率。(希尔是个人名。。。)
改进的地方:插入算法中,如果一个数据比较小而居于最右边,那么它需要一个一个地移动所有中间的数据项,效率比较低。
希尔排序通过加入插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能大跨度地移动。当这些数据项排过一趟序后,减小数据项的间隔。再进行排序,依次进行下去。间隔被称为增量,用h表示.
进行过几次增量排序后,所有的元素离它再最终有序序列中的位置相差不大,数组达到"基本有序",这时再来插入排序,移动量非常小。
当h值很大的时候,数据项每一趟排序需要移动元素的个数很少,但数据项移动的距离很长,这是非常有效率的。当h减少时,每一趟排序需要移动的元素的个数增多,但是此时数据项已经接近于它们排序后最终的位置,这对于插入排序可以更有效率。
其中,h = h x 3 +1, h = (h -1) / 3,是经验值。