本文中的3中排序方法是最基本的,效率并不高。以后会提到其他效率较高的高级排序算法。
冒泡BubbleSort
例如给定如下数列:
[1,3,2,8,12,6]
比较第1个元素“1”和第2个元素“3”,“1”比“3”小,所以不做改变。接着比较第2个元素“3”和第3个元素“2”,“3”比“2”大,交换他们的位置:
[1,2,3,8,12,6]
接着比较第3个元素“3”和第4个元素“8”,“3”比“8”小,不做改变。
接着比较第4个元素“8”和第5个元素“12”,“8”比“12”小,不做改变。
接着比较第5个元素“12”和第6个元素“6”,“12”比“6”小,交换他们的位置:
[1,2,3,8,6,12]
这样比较完一次后,最大的元素就被移到了数列的末尾。如此反复这个过程n-1次后,整个数列就排序好了。
由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。(因为水压的缘故,气泡总是越接近水面越大~)
其实这种思想很直接,就是遍历整个数列,将每次找到的最大数移到末尾。这样末尾慢慢会形成一个局部有序结构。
因此,如果每次遍历整个数列,将找到的最小值移到数组开端是一样的效果。
同时这种算法不需要额外的存储。
这种算法的时间复杂度为O(n^2)
public static void main(String[] args) {
Integer[] a = {4,7,3,1,5,34,24,65,78,12,9};
Integer x = 0;
for(int j = 0;j<a.length-1;j++){ //这里是j<length,而下面是i<length-1是因为下面有a[i+1],如果使用i<length则会数组越界。
for(int i = 0; i<a.length-1;i++){
if(a[i] > a[i+1] && i < length-j){ //这里如果外层的j已经循环过j次了,那么就会在数组右端形成一个局部有序的部分,下次再排序时就不需要再比较这部分的元素了。
x = a[i+1];
a[i+1] = a[i];
a[i] = x;
System.out.println(Arrays.toString(a));
}else{
System.out.println(Arrays.toString(a));
continue;
}
}
}
}
选择排序
[7,3,2,8,12,6]
将数组第一个值7记录在一个变量中后,和下一个元素比较。当7大于第二个元素时,变量改为最新找到的最小值,如果7小于第二个元素,则变量值不变。
这样在一次循环后,找到该数组最小值,第三个元素2。
之后将最小元素和数组左边第一个元素对换:
[2,3,7,8,12,6]
考虑到数组左端已经是排序过的了,所以下次循环时不再考虑。因此下次循环从第二个元素3开始。
3是最小值,因此不变。然后剩下的元素中6是最小值,和数组左端未排序部分的第一个元素7对换:
[2,3,6,8,12,7]
一直重复上面的步骤,即可完成排序。
因此选择排序和冒泡排序的思想没有本质不同,都是遍历数组,找到一个极端值后放到数组的头或尾。
区别就在于选择排序用一个变量来记录找到的最小值,并且找到后只交换一次,而不是和冒泡一样,前后元素不停的交换。
因此选择排序节约的是交换时间,比较次数并没有减少。因此时间复杂度还是O(n^2)
插入排序
给定数组:[3,2,1,4,9,10,5]
插入算法的过程是先找出数组中的局部有序的部分。例如[3,6,7,4,8,9]那么[3,6,7]就是局部有序的部分。
不过像[3,2,1,4,9,10,5]这样的数组就没有局部有序的部分,所以要从头开始。
首先3保持不变,将2标记为需要插入的元素。2比3小,首先将2记录到变量里,将2所在的位置预留给左端向右移动的元素,然后将2插入到3之前,3向右移动一位,占了2以前的位置:
[2,3,1,4,9,10,5]
之后1被标记为需要插入的元素。将1插入到2之前。2和3向右移动...这样数组左边的有序部分不断增加,无序部分不断减少。
因此这个算法的思想就是在数组左侧形成一个局部有序的区域。这个算法最大的好处就是,每次需要移动的元素数量是随机的,和n没有直接关系。
根据需要比较的次数,时间复杂度仍然是O(n^2),但是插入排序是比冒泡和选择都更好一点的算法。
插入算法还可以用于解决一类问题,就是去掉重复值的问题--由于需要将标记的元素插入到数组左端局部有序的部分中,所以可以在查询插入位置时,判断是否有重复值。
当发现重复值时,可以:
将标记的元素修改为某极端值,例如正数数组,就设为-1. 然后不做任何移动。继续对下一个元素排序。随着有序部分的增加,值为-1的元素逐渐被移到数组的右端,排序完成后一并抛弃掉。
关于对象的排序
很多时候数组里不是基本的数据类型,而是存的对象。此时需要解决的第一个问题就是,如何判断两个对象的先后顺序,解决了这个问题以后,我们就可以使用上面的3中基本排序方法来排序数组了。
例如数组中存储了person类的对象。我们准备让这个数组按每个对象的年龄来排序。因此可以在person类中新增一个方法:
public Person comparePerson(Person person1, Person person2){
if (person1.getAge() > person2.getAge(){
return person1;
}
return person2;
}
之后每次需要比较数组元素时都调用这个方法,就可以实现排序了。
还有一种基本排序方法为奇偶排序,在多cpu的环境效率比较高。