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

来源:互联网 发布:绝地求生加速器 知乎 编辑:程序博客网 时间:2024/05/17 02:47

    • 1冒泡排序
      • 1原理
      • 2代码
      • 3复杂度
    • 2选择排序
      • 1原理
      • 2代码
      • 3复杂度
    • 3插入排序
      • 1原理
      • 2代码
      • 3复杂度
    • 4三个排序算法的小结与比较

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
原创粉丝点击