美文网首页
冒泡、插入、选择

冒泡、插入、选择

作者: Rreply | 来源:发表于2019-01-16 23:47 被阅读7次

插入排序和冒泡排序的时间复杂度都是O(n^2),但是为什么实际使用上,我们更加倾向于使用插入排序呢?

如何分析一个“排序算法”?

需要从以下三个方面来考虑。

  • 算法的执行效率
    1. 最好、最坏、平均时间复杂度
    2. 时间复杂度的常数、系数、低阶
    3. 比较次数和交换(或移动)次数
  • 排序算法的内存消耗
  • 排序算法的稳定性

首先从算法的执行效率方面来说:

  1. 有时候我们为了更好地对算法的优劣进行排序对比,需要将其三种时间复杂度都计算出来。
  2. 我们用来计算的数据有时候就是有序的或者逆序的,这个时候就必须使用最好或者最坏时间复杂度来进行对比。
  3. 虽然我们平时会忽略掉常数、系数、低阶,但是如果我们进行计算的数据并不是特别大,就不应该忽略它们。例如O(n) +20,在n不大的情况下,常数项就必须纳入考虑范围内了。
  4. 一般情况下我们忽略这些操作,但是对于冒泡、插入、选择来说,它们是基于比较的算法,所以比较和交换就不能够被忽略。

然后从算法的内存消耗上来说:
内存消耗也可以理解成为空间复杂度,对于这三种算法来说,他们的都是原地排序算法,所以其空间复杂度为O(1)。但是对于其他非原地排序的算法来说,是否需要牺牲空间来换取时间效率就成为一个重要的考量因素。
最后从算法的稳定性来说:
可能有一些人觉得算法稳定性并没有多大多用,但是举个例子,如果双十一,老板要求我们按照价格和时间的顺序把商品排好。有两种方法,一种就是先按照价格排序,然后同一价格再按照时间排序。还有一种方法就是使用稳定的算法先用时间排序,然后价格排序。

有个APP叫数据结构和算法教程能够很好的展示这三种排序动画,所以就只给出源码了。

冒泡排序

import java.util.Arrays;

public class BubbleSort {

    TestItem[] array;

    BubbleSort(TestItem[] array) {
        this.array = array;
    }

    boolean sort() {
        if (array.length == 0 || array.length == 1) {
            return false;
        }

        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j].num > array[j + 1].num) {
                    TestItem temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                }
            }
        }
        return true;
    }

    public static void main(String[] args) {
        TestItem[] test = new TestItem[30];
        for (int i = 0; i < 30; i++) {
            test[i] = new TestItem(Math.random() * 10);
        }
        BubbleSort bubbleSort = new BubbleSort(test);
        bubbleSort.sort();
        System.out.println(Arrays.toString(bubbleSort.array));

    }

    static class TestItem {

        double num;

        TestItem(double a) {
            num = a;
        }

        @Override
        public String toString() {
            return "num为: " + num;
        }
    }
}

选择排序

public class SelectSort {

    int[] array;

    SelectSort(int[] array){
        this.array = array;
    }

    boolean sort(){
        if (array.length == 0 || array.length ==1){
            return false;
        }
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = i + 1; j < array.length; j++) {
                if (array[i] > array[j]){
                    int temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                    j++;
                }
            }
        }
        return true;
    }
}

插入排序

import java.util.Arrays;

public class InsertSort {

    int[] array;

    InsertSort(int[] array){
        this.array = array;
    }

    boolean sort(){
        if (array.length == 0 || array.length == 1){
            return false;
        }
        for (int i = 1; i < array.length; i++) {
            if (array[i] < array[i -1]){
                int temp = array[i];
                for (int j = i-1; j >= 0 ; j--) {
                    if (array[j] > temp){
                        array[j + 1] = array[j];
                    } else {
                        array[j] = temp;
                        break;
                    }
                }
            }
        }
        return true;
    }

    public static void main(String[] args) {
        int[] array = {1, 2,1,312,4234,2,41,3,123,1,31,312,3,123,1,31,3,131,3,};
        InsertSort sort = new InsertSort(array);
        sort.sort();
        System.out.println(Arrays.toString(sort.array));
    }
}

三个算法的比较

排序算法 冒泡排序 选择排序 插入排序
最好时间复杂度 O(n) O(n^2) O(n)
最坏时间复杂度 O(n^2) O(n^2) O(n^2)
平均时间复杂度(使用逆序度计算) O(n^2) O(n^2) O(n^2)
是否稳定
是否是原地排序(空间复杂度O(1)

回到开题,为什么我们倾向于使用插入排序而不是冒泡排序呢?从表上来看,两种排序的时间复杂度都是一样的啊。
请看下面对比

//冒泡排序
for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j].num > array[j + 1].num) {
                    //每次循环需要执行下面三行代码
                    TestItem temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                }
            }
        }
//插入排序
 for (int i = 1; i < array.length; i++) {
            if (array[i] < array[i -1]){
                int temp = array[i];
                for (int j = i-1; j >= 0 ; j--) {
                    if (array[j] > temp){
                        //每次循环只需要执行一次代码
                        array[j + 1] = array[j];
                    } else {
                        array[j] = temp;
                        break;
                    }
                }
            }
        }

如果不把比较计算在内的话,冒泡比插入需要多消耗两倍的时间,考虑比较的话,需要多消耗一倍的时间,所以这也就是我们为什么使用插入而不是冒泡的原因了。

相关文章

网友评论

      本文标题:冒泡、插入、选择

      本文链接:https://www.haomeiwen.com/subject/plcvdqtx.html