基本排序算法

本文中的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的环境效率比较高。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值