1.快速排序算法
事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。
步骤:
· 从数列中挑出一个元素,称为 "基准"(pivot),
· 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
· 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
public void quicksort(int[] pnArray, int left, int right){
if(left < right){
int key = pnArray[left];
int low = left;
int high = right;
while(low < high){
while(low < high && pnArray[high] >= key){
high--;
}
pnArray[low] = pnArray[high];
while(low < high && pnArray[low] < key){
low++;
}
pnArray[high] = pnArray[low];
}
pnArray[low] = key;
quicksort(pnArray,left,low-1);
quicksort(pnArray,low+1,right);
}
return;
}
分析
快速排序的时间主要耗费在划分操作上,对长度为k的区间进行划分,共需k-1次关键字的比较。
最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。时间复杂度为O(n*n)
在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:O(nlgn)
尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。
2.选择排序
选择排序的思想非常直接,不是要排序么?那好,我就从所有序列中先找到最小的,然后放到第一个位置。之后再看剩余元素中最小的,放到第二个位置……以此类推,就可以完成整个的排序工作了。可以很清楚的发现,选择排序是固定位置,找元素。相比于插入排序的固定元素找位置,是两种思维方式。不过条条大路通罗马,两者的目的是一样的。
public void sort(int[] v){
for(int i=0; i<v.length; i++){
int min = v[i];
int temp;
int index = i;
for(int j=i+1;j<v.length;j++){
if(v[j] < min){
min = v[j];
index = j;
}
}
temp = v[i];
v[i] = min;
v[index]= temp;
}
return;}
分析:从选择排序的思想或者是上面的代码中,我们都不难看出,寻找最小的元素需要一个循环的过程,而排序又是需要一个循环的过程。因此显而易见,这个算法的时间复杂度也是O(n*n)的。这就意味值在n比较小的情况下,算法可以保证一定的速度,当n足够大时,算法的效率会降低。并且随着n的增大,算法的时间增长很快。因此使用时需要特别注意。
3.冒泡排序
冒泡排序也是最简单最基本的排序方法之一。冒泡排序的思想很简单,就是以此比较相邻的元素大小,将小的前移,大的后移,就像水中的气泡一样,最小的元素经过几次移动,会最终浮到水面上。
public void bubble_sort(int[] v){
bool exchange;
for (int i=0; i<v.length; i++){
int temp = 0;
exchange = false;
for(int j=v.length-1; j>i+1; j--){
if (v[j] < v[j-1]){
temp = v[j];
v[j] = v[j-1];
v[j-1] = temp;
exchange = true;
}
}
if (!exchange){
break;
}
}
return;
}
分析:因为每一趟排序都使有序区增加了一个气泡,在经过n-1趟排序之后,有序区中就有n-1个气泡,而无序区中气泡的重量总是大于等于有序区中气泡的重量,所以整个冒泡排序过程至多需要进行n-1趟排序。以此本算法的时间复杂度还是O(n*n),也不能算是一个高效的算法。
4.插入排序
插入排序的思想有点像打扑克抓牌的时候,我们插入扑克牌的做法。想象一下,抓牌时,我们都是把抓到的牌按顺序放在手中。因此每抓一张新牌,我们都将其插入到已有的排好序的手牌当中,注意体会刚才的那句话。也就是说,插入排序的思想是,将新来的元素按顺序放入一个已有的有序序列当中.
public void Insert_sort(int[] v){
for (int i=1; i<v.length; i++){
int key = v[i];
int j = i-1;
while (j >= 0 && v[j] > key){
v[j+1] = v[j];
j--;
}
v[j+1] = key;
}
return;
}
分析:插入排序的思路很简单,很清晰,是一种最常见最简单的排序方法。但是可以看出,由于需要两层循环,外层循环n-1次,内层循环每次递增一次。当输入完全从小到大有序时,只需要常数的时间,这当然是最好的情况。但是我们不能期望输入,当输入完全逆序时,最坏的情况就出现了,显然时间复杂度是O(n*n)的。我们都很清楚,这个时间复杂度在排序中并不能算好的。这也是为什么插入排序虽然简单,但并没有被广泛应用的原因所在。