O(N2)的三种排序-- 冒泡、选择、插入(java代码)


1、冒泡排序

1)原理

a、比较相邻两个元素
b、如果满足交换要求(视排序规定而定,本文均按照从左到右、从小到大排序),则交换位置
c、向后移动一位,回到a步骤继续
在算法执行时,最大的数据项总是“冒泡”到数组的最后端,因此被称为冒泡排序。

2)代码

//冒泡排序
public void  BubbleSort(long[] a){
    int out, in;
    int nElems = a.length;

    System.out.println("排序前: ");
    display(a);
    for (out =nElems-1; out>1; out -- ){
    //外层for是控制冒泡的总体次数
    //out>1是因为之前的n-2次排序是有序的,所以最后一个元素无需再排序即为有序
        for(in = 0; in<out; in++){//内层则是冒泡的一次过程
            if( a[in] > a[in + 1]){
            //相邻两数之间进行比较,如果条件成立,则交换两数
                swap(a, in, in+1);
            }// end of if
        }// end of for(in)
        System.out.print(nElems - out+":");
        display(a);
    }//end of for(out)
}//end of BubbleSort

实际上,使用swap函数来交换式为了让程序更加直观,调用swap函数会增加开销,所以在实际使用中,可以视情况而定将交换过程直接写在判断语句块中。
测试结果:

现在测试冒泡排序:
排序前: 
5  4  1  3  6  8  2  9  0  7  
1:4  1  3  5  6  2  8  0  7  9  
2:1  3  4  5  2  6  0  7  8  9  
3:1  3  4  2  5  0  6  7  8  9  
4:1  3  2  4  0  5  6  7  8  9  
5:1  2  3  0  4  5  6  7  8  9  
6:1  2  0  3  4  5  6  7  8  9  
7:1  0  2  3  4  5  6  7  8  9  
8:0  1  2  3  4  5  6  7  8  9  
运行时间为:4毫秒

3)复杂度

假设数组中有N个数据项,那么第一趟排序进行了N-1次比较,第二趟进行了N-2次比较,以此类推,可以得到:

N-1)+(N-2)+(N-3)+....+1=N*(N-1)/2

而交换次数显然少于比较的次数,得出结论,冒泡排序运行需要O(n2)时间级别。

2、选择排序

1)原理

注:依旧是从左到右从小到大排序
a、从数组无序部分左到右,元素依次与最小值比较(初始的最小值为第一个元素,即标志flag为0),若元素比当前最小值小,则更新标志flag为当前最小的元素的下标
b、遍历完之后,将a[flag]元素与有序部分的后一个元素交换
c、回到步骤a直至数组全部有序

2)代码

public void SelectSort(long[] a){
    int nElems = a.length;
    int out, in,min;

    System.out.println("排序前: ");
    display(a);
    for(out=0; out<nElems-1; out++){
        min = out; //记录最小值的下标
        for(in=out+1; in<nElems; in++){
            if(a[in] < a[min]){
                min = in;
            }//end of if
        }//end of for(in)
        if(min != out){             
        //减少交换次数,消耗的时间更少
            swap(a,out,min);
        }
        System.out.print((out+1)+":");
        display(a);
    }//end of for(out)
}//end of SelectSort

测试结果:

现在测试选择排序:
排序前: 
5  4  1  3  6  8  2  9  0  7  
1:0  4  1  3  6  8  2  9  5  7  
2:0  1  4  3  6  8  2  9  5  7  
3:0  1  2  3  6  8  4  9  5  7  
4:0  1  2  3  6  8  4  9  5  7  
5:0  1  2  3  4  8  6  9  5  7  
6:0  1  2  3  4  5  6  9  8  7  
7:0  1  2  3  4  5  6  9  8  7  
8:0  1  2  3  4  5  6  7  8  9  
9:0  1  2  3  4  5  6  7  8  9  
运行时间为:3毫秒

3)复杂度

a、首先,在选择排序中,下标小于等于out的位置的数据项总是有序的
b、选择排序和冒泡排序执行了相同次数的比较,但是交换次数更少。
c、虽然选择和冒泡一样,复杂度都是O(n2),但是当N值较小时,而且交换时间级比比较时间级大得多时,选择排序还是要快得多的

3、插入排序

1)原理

a、局部有序:在冒泡和选择排序中,数据项在某个时刻是完全有序的,而在插入排序中,某组数据仅仅是局部有序的
b、被标记的元素:在这个元素的左侧(依据本文的情况)是有序的,从这个元素开始之后的元素是尚未被排序的。
c、插入排序:从被标记元素开始将其按照顺序插入前面的有序序列。被标记的元素依次与前面的有序的元素比较,若有序元素比该标记大,则向右移动一位,直到有序序列中的元素不大于被标记元素或数组到头。然后将这个被标记的元素插入相应位置

2)代码

public void InsertSort(long[] a){
    //具有局部有序的特征
    //被标记的元素(比如,规定被标记的元素右边是无序的,其左边是有序的
    int out,in;
    int nElems=a.length;

    System.out.println("排序前: ");
    display(a);
    for(out = 1; out<nElems; out++){
        long tmp = a[out];//tmp就是标记元素
        in = out;
        while(in >0 && a[in-1] >= tmp){
            //每一趟循环将把比标记元素大的元素后移一位,比较跟移位同步进行,节省了时间
            //当in=0或遇到比标记元素小的元素时跳出循环,此时in即为标记元素在有序序列的位置
            a[in] = a[in-1];

            System.out.print("被标记的元素为="+tmp+"内循环(比较的有序元素) i="+in+":");
            display(a);
            in--;
        }
        a[in] = tmp;//插入被标记元素
        System.out.print(out+":");
        display(a);
    }//end of for(out)
}//end of InsertSort

测试结果

现在测试插入排序:
排序前: 
5  4  1  3  6  8  2  9  0  7  
被标记的元素为4;比较的有序元素> i=15  5  1  3  6  8  2  9  0  7  
14  5  1  3  6  8  2  9  0  7  
被标记的元素为1;比较的有序元素> i=24  5  5  3  6  8  2  9  0  7  
被标记的元素为1;比较的有序元素> i=14  4  5  3  6  8  2  9  0  7  
21  4  5  3  6  8  2  9  0  7  
被标记的元素为3;比较的有序元素> i=31  4  5  5  6  8  2  9  0  7  
被标记的元素为3;比较的有序元素> i=21  4  4  5  6  8  2  9  0  7  
31  3  4  5  6  8  2  9  0  7  
41  3  4  5  6  8  2  9  0  7  
51  3  4  5  6  8  2  9  0  7  
被标记的元素为2;比较的有序元素> i=61  3  4  5  6  8  8  9  0  7  
被标记的元素为2;比较的有序元素> i=51  3  4  5  6  6  8  9  0  7  
被标记的元素为2;比较的有序元素> i=41  3  4  5  5  6  8  9  0  7  
被标记的元素为2;比较的有序元素> i=31  3  4  4  5  6  8  9  0  7  
被标记的元素为2;比较的有序元素> i=21  3  3  4  5  6  8  9  0  7  
61  2  3  4  5  6  8  9  0  7  
71  2  3  4  5  6  8  9  0  7  
被标记的元素为0;比较的有序元素> i=81  2  3  4  5  6  8  9  9  7  
被标记的元素为0;比较的有序元素> i=71  2  3  4  5  6  8  8  9  7  
被标记的元素为0;比较的有序元素> i=61  2  3  4  5  6  6  8  9  7  
被标记的元素为0;比较的有序元素> i=51  2  3  4  5  5  6  8  9  7  
被标记的元素为0;比较的有序元素> i=41  2  3  4  4  5  6  8  9  7  
被标记的元素为0;比较的有序元素> i=31  2  3  3  4  5  6  8  9  7  
被标记的元素为0;比较的有序元素> i=21  2  2  3  4  5  6  8  9  7  
被标记的元素为0;比较的有序元素> i=11  1  2  3  4  5  6  8  9  7  
80  1  2  3  4  5  6  8  9  7  
被标记的元素为7;比较的有序元素> i=90  1  2  3  4  5  6  8  9  9  
被标记的元素为7;比较的有序元素> i=80  1  2  3  4  5  6  8  8  9  
90  1  2  3  4  5  6  7  8  9  
运行时间为:8毫秒

3)复杂度

第一趟比较最多需要1次,第二趟最多2次,以此类推,有

1+2+3+...+(N-1)=N*(N-1)/2

而在发现插入点之前,平均只有数据量的一半进行了比较,所有又得到比较次数为

N*(N-1)/4

复制移动数据的次数大致等于比较的次数,然而复制移动与交换的时间消耗不同,所以插入排序虽然时间复杂度是O(N2)级别的,但是要比冒泡排序快一倍,比选择排序略快一些(PS:测试结果中由于加入了很多输出语句,所以实际运行时间较多)。

4、三个排序算法的小结与比较

排序方法平均情况最好情况最坏情况空间复杂度稳定性复杂性
插入排序O(N2)O(N2)O(N)O(1)稳定简单
冒泡排序O(N2)O(N2)O(N)O(1)稳定简单
选择排序O(N2)O(N2)O(N2)O(1)不稳定简单
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值