手记

常用三大排序算法

1:冒泡排序

冒泡排序是的算法思路是将最小数值放在下标为0的位置,将最大值放在mao.length-1的位置

外层for循环开始计算层数,即mao.length-1为目标计划循环次数,当外层for完成一次后,总长度就会-1,也就是说最大值已经出来了并且放在了最后一位,那么在之后的循环中就不算这一项了,以此类推。

内层for循环从下标0开始,和下一角标比较,这里用第一项和第二项代表,如果第一项>第二项,那么两个就互换,否侧不换,然后第二项和第三项比较。直到完成这一次循环。

// 简单排序--冒泡排序
        int[] mao = { 120, 110, 100, 90, 80, 70, 45 };
        // 外循环只是循环几次
        for (int i = mao.length - 1; i > 1; i--) {
            // 内循环每循环一次就会有拍好一位,如最大值
            System.out.println("第" + (mao.length - i) + "次循环");
            for (int j = 0; j < i; j++) {
                if (mao[j] > mao[j + 1]) {
                    // 如果第一个比第二大,那就换位子
                    int temp = mao[j];
                    mao[j] = mao[j + 1];
                    mao[j + 1] = temp;
                }
                for (int k = 0; k < mao.length; k++) {
                    System.out.print(mao[k] + " , ");
                }
                System.out.println();
            }

        }
第1次循环
110 , 120 , 100 , 90 , 80 , 70 , 45 , 
110 , 100 , 120 , 90 , 80 , 70 , 45 , 
110 , 100 , 90 , 120 , 80 , 70 , 45 , 
110 , 100 , 90 , 80 , 120 , 70 , 45 , 
110 , 100 , 90 , 80 , 70 , 120 , 45 , 
110 , 100 , 90 , 80 , 70 , 45 , 120 , 
第2次循环
100 , 110 , 90 , 80 , 70 , 45 , 120 , 
100 , 90 , 110 , 80 , 70 , 45 , 120 , 
100 , 90 , 80 , 110 , 70 , 45 , 120 , 
100 , 90 , 80 , 70 , 110 , 45 , 120 , 
100 , 90 , 80 , 70 , 45 , 110 , 120 , 
第3次循环
90 , 100 , 80 , 70 , 45 , 110 , 120 , 
90 , 80 , 100 , 70 , 45 , 110 , 120 , 
90 , 80 , 70 , 100 , 45 , 110 , 120 , 
90 , 80 , 70 , 45 , 100 , 110 , 120 , 
第4次循环
80 , 90 , 70 , 45 , 100 , 110 , 120 , 
80 , 70 , 90 , 45 , 100 , 110 , 120 , 
80 , 70 , 45 , 90 , 100 , 110 , 120 , 
第5次循环
70 , 80 , 45 , 90 , 100 , 110 , 120 , 
70 , 45 , 80 , 90 , 100 , 110 , 120 , 
第6次循环
45 , 70 , 80 , 90 , 100 , 110 , 120 , 

算法不变性

这里可以看出当比较到最后一次时,只剩下前两个比较了,也就是说在最右边一旦完成比较后就不会再进行任何比较操作,当然每个排序的不变性是不一样的。

冒泡排序的效率

在上面的例子结果中可以看出,7条数据的情况下,第一趟循环比较了6次,第二次比较了5次...,最后一次比较一次,公式为
(N-1)+(N-2)+(N-3)+...+1===N*(N-1)/==2====

比较算法结果:

学过高数的人都知道,数学中有叫趋向于某某某的说法,咱们的算法比较结果就是趋向于==N²/2==
。因为在N很大的情况下,N-1与N可以都看做为N,就算-1,-2也影响不大。

交换算法结果

由于在比较时不一定要交换,那么交换次数肯定是小于比较次数,有点的是从头到位都需要交换(逆向排序),则有的一次都不用换(已经排序好的),所以,这里排序算法取平均==N²/4==.

大O表示法

由于常数不算在表示法中,所以结果为O(N²)时间。

在平常的代码中,如果那你看到双重for循环时,基本就可以判断运行时间效果为O(N²)了。

2:选择排序

// 选择排序
        System.out.println("选择排序");

        int[] selectInt = { 120, 110, 100, 90, 80, 70, 45 };
        /**
         * 第一步: 原理是都和第一项(下标0)比较,小的则替换
         * 
         * 外层变量设置为outer,内层变量为inner,最小数据存放下标min。
         * selectInt.length-1一共7个数比6次就可以了。selectInt.length内部是从下标1开始的,这里就不要再-1了,
         * 否侧少比较一次
         */
        int min;
        for (int outer = 0; outer < selectInt.length - 1; outer++) {
            min = outer;
            // 内循环每循环一次就会有拍好一位,如最大值
            System.out.println("第" + (outer + 1) + "次循环");
            for (int inner = outer + 1; inner < selectInt.length; inner++) {
                // 如果后六个数中,有小于第一项(下标0)的,就调换下标
                if (selectInt[inner] < selectInt[min]) {
                    min = inner;
                }
            }
            int temp = selectInt[outer];
            selectInt[outer] = selectInt[min];
            selectInt[min] = temp;

            for (int k = 0; k < selectInt.length; k++) {
                System.out.print(selectInt[k] + " , ");
            }
            System.out.println();
        }
        /**
         * 第1次循环 45 , 110 , 100 , 90 , 80 , 70 , 120 ,
         * 
         */
第1次循环
45 , 110 , 100 , 90 , 80 , 70 , 120 , 
第2次循环
45 , 70 , 100 , 90 , 80 , 110 , 120 , 
第3次循环
45 , 70 , 80 , 90 , 100 , 110 , 120 , 
第4次循环
45 , 70 , 80 , 90 , 100 , 110 , 120 , 
第5次循环
45 , 70 , 80 , 90 , 100 , 110 , 120 , 
第6次循环
45 , 70 , 80 , 90 , 100 , 110 , 120 , 

选择排序算法不变性

下标小于等于outer的位置的数据总是有序的,一旦放在前面后,就不会再动。

选择排序算法效率

上面结果可以说明,虽然也是比较了和冒泡一样多的次数,但是交换缺少了很多。所以时间为N²/2

比较次数和交换次数

比较次数:N²/2
交换次数:最多N-1,最少0次,取平均N/2

大O表示法

O(N²)

以上可看出选择排序是比冒泡排序快点的。

3:插入排序

概念:有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)

/**
         * 从第二项开始,第一项默认为有序 
         * 1:把第二项数据暂存,和第一项比较,如果第一项>第二项则调换,
         * 2:把第三项数据暂存,和第二项比较,如果第二项>第三项则调换, 这时调换后的第二项还要和第一项比较,然后再判断调换,从当前下标开始向左遍历凡是大于temp的数据,下标
         * 都均向右移动一位(调换)。
         * 以此类推 ........
         *
         * 
         * 很多人估计不理解为什么从第二项开始,而不是从第一项,
         * 这里我稍微做一下解释,插入排序就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,
         * 我们对于一个数组,不知道哪里是排序好的,可能是前三条,也可能不是有序的,我们这时就要假设一段已经排好序的数组,我们直接取前三项的话,
         * 不一定是排序好的, 我们取前一项的话,就一个数据肯定是排序好的,所以就从第二项开始,默认第一项已经排序好了。这就是由来。
         */
        int insertElems[] = { 23, 4, 5, 34, 56, 21 };
        int inner, outer;
        for (outer = 1; outer < insertElems.length; outer++) {
            int temp = insertElems[outer];// 把第二项暂存
            inner = outer;
            // (第一项>第二项)
            while (inner > 0 && insertElems[inner - 1] > temp) {
                insertElems[inner] = insertElems[inner - 1];
                inner--;
            }
            insertElems[inner] = temp;
            for (int k = 0; k < insertElems.length; k++) {
                System.out.print(insertElems[k] + " , ");
            }
            System.out.println();

        }
4 , 23 , 5 , 34 , 56 , 21 , 
4 , 5 , 23 , 34 , 56 , 21 , 
4 , 5 , 23 , 34 , 56 , 21 , 
4 , 5 , 23 , 34 , 56 , 21 , 
4 , 5 , 21 , 23 , 34 , 56 , 

插入排序的不变性

在每趟结束时,在temp位置插入后,比outer下标小(左边)的数据项都是有序的。

结论:插入排序比冒泡快一倍,优于选择排序

0人推荐
随时随地看视频
慕课网APP