【算法】冒泡排序+选择排序+插入排序

目录

前言

1.冒泡排序

2.选择排序

3.插入排序


前言

本篇文章讲解了冒泡排序,选择排序和插入排序的算法(思路讲解+动画+代码 (java语言) ),这是三种简单的排序。

 

1.冒泡排序

1.1冒泡排序思路

依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面。

1.2冒泡排序图解

1.3冒泡排序代码实现

//冒泡排序,可直接运行
public class BubbleSort {

    public static void main(String[] args) {
        int[] arr = {9,6,14,2,55,12,34,7};
        sortByBubble(arr);
        for (int i = 0;i < arr.length;i++){
            System.out.print(arr[i]+" ");
        }
    }

    private static void sortByBubble(int[] arr) {
        if (arr==null || arr.length < 2){
            return;
        }
        boolean flag;
        for (int i = 0;i < arr.length;i++){
            flag = false;
            //此处-1是为了防止数组下标溢出 a[j+1]
            //若不减一,此处的例子上a[j+1]则会变成a[a.length]
            for (int j = 0;j < arr.length - i - 1;j++){
                int temp;
                if (arr[j] > arr[j+1]){
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                    flag = true;
                }
            }
            if (!flag){
                break;
            }
        }
    }

}

第一趟排序:

  •       第一次排序:9和6比较, 9大于6,交换位置         [9,6,14,2,55,12,34,7]
  •       第二趟排序:9和14比较,9小于14,不交换位置     [6,9,14,2,55,12,34,7]
  •       第三趟排序:14和2比较,14大于2,交换位置         [6,9,2,14,55,12,34,7]
  •       第四趟排序:14和55比较,14小于55,不交换位置  [6,9,2,14,55,12,34,7]
  •         第五趟排序:55和12比较,55大于12,交换位置    [6,9,2,14,12,55,34,7]
  •       第六趟排序:55和34比较,55大于34,交换位置   [6,9,2,14,12,34,55,7]
  •                       第七趟排序:55和7比较,55大于7,交换位置       [6,9,2,14,12,34,7,55]
  •       第一趟总共进行了七次比较,排序结果: [6,9,2,14,12,34,7,55]

第二趟排序:

  •       第一次排序:6和9比较,6小于9,不交换位置     [6,9,2,14,12,34,7,55]
  •       第二趟排序:9和2比较,9大于2, 交换位置          [6,2,9,14,12,34,7,55]
  •       第三趟排序:9和14比较,9小于14,不交换位置   [6,2,9,14,12,34,7,55]
  •       第四趟排序:12和14比较,14大于12,交换位置   [6,2,9,12,14,34,7,55]
  •         第五趟排序:14和34比较,14小于34,不交换位置   [6,2,9,12,14,34,7,55]
  •       第六趟排序:34和7比较,34大于7,交换位置    [6,2,9,12,14,7,34,55]
  •       第二趟总共进行了六次比较,排序结果: [6,2,9,12,14,7,34,55]

第三趟排序:

  •       第一次排序:6和2比较,6大于2,交换位置      [2,6,9,12,14,7,34,55]
  •       第二趟排序:6和9比较,6小于9,不交换位置     [2,6,9,12,14,7,34,55]
  •       第三趟排序:9和12比较,9小于12,不交换位置  [2,6,9,12,14,7,34,55]
  •       第四趟排序:12和14比较,12小于14,不交换位置 [2,6,9,12,14,7,34,55]
  •         第五趟排序:14和7比较,14大于7,交换位置    [2,6,9,12,7,14,34,55]
  •       第三趟总共进行了五次比较,排序结果: [2,6,9,12,7,14,34,55]

第四趟排序:

  •       第一次排序:2和6比较,2小于6,不交换位置      [2,6,9,12,7,14,34,55]
  •       第二趟排序:6和9比较,6小于9,不交换位置         [2,6,9,12,7,14,34,55]
  •       第三趟排序:9和12比较,9小于12,不交换位置     [2,6,9,12,7,14,34,55]
  •       第四趟排序:12和7比较,12大于7,交换位置         [2,6,9,7,12,14,34,55]
  •       第四趟总共进行了四次比较,排序结果: [2,6,9,7,12,14,34,55]

第五趟排序:

  •       第一次排序:2和6比较,2小于6,不交换位置      [2,6,9,7,12,14,34,55]
  •       第二趟排序:6和9比较,6小于9,不交换位置         [2,6,9,7,12,14,34,55]
  •       第三趟排序:9和7比较,9大于12,交换位置           [2,6,7,9,12,14,34,55]
  •       第五趟总共进行了三次比较,排序结果: [2,6,7,9,12,14,34,55]

第六趟排序:

  •       第一次排序:2和6比较,2小于6,不交换位置      [2,6,7,9,12,14,34,55]
  •       第二趟排序:6和7比较,6小于7,不交换位置         [2,6,7,9,12,14,34,55]
  •       第六趟总共进行了二次比较,排序结果: [2,6,7,9,12,14,34,55]

第七趟排序:

  •       第一次排序:2和6比较,2小于6,不交换位置      [2,6,7,9,12,14,34,55]
  •       第七趟总共进行了一次比较,排序结果: [2,6,7,9,12,14,34,55]

1.4冒泡排序的时间复杂度分析

在设置标志变量之后:

当原始序列“正序”排列时,冒泡排序总的比较次数为n-1,移动次数为0,也就是说冒泡排序在最好情况下的时间复杂度为O(n);

当原始序列“逆序”排序时,冒泡排序总的比较次数为n(n-1)/2,移动次数为3n(n-1)/2次,所以冒泡排序在最坏情况下的时间复杂度为O(n^2);

当原始序列杂乱无序时,冒泡排序的平均时间复杂度为O(n^2)。

1.5冒泡排序的空间复杂度

冒泡排序排序过程中需要一个临时变量进行两两交换,所需要的额外空间为1,因此空间复杂度为O(1)

2.选择排序

2.1选择排序思路

1.每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,

如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引出的值为最小值, 最后可以找到最小值所在的索引

2.交换第一个索引处和最小值所在的索引处的值.

2.2选择排序图解

2.3选择排序代码实现

/**
 * 选择排序,可直接运行
 */
public class SelectionSort {

    public static void main(String[] args) {
        int[] arr = {9,6,14,2,55,12,34,7};
        sortBySelect(arr);
        for (int i = 0;i < arr.length;i++){
            System.out.print(arr[i]+" ");
        }
    }

    private static void sortBySelect(int[] arr) {
        if (arr ==null || arr.length < 2){
            return;
        }
        for (int i = 0; i < arr.length -1; i++) {
            //假定本次遍历,最小值所在的索引是i
            int min = i;
            int temp;
            //需要理解这里的 i+1 和 j < arr.length
            for (int j = i+1; j < arr.length; j++) {
                if (arr[min] > arr[j]){
                    //找到一个比自己小的就把它记下来,
                    //接着再用这个比自己小的和后续的比较
                    min = j;
                }
            }
            //此时arr[min]为最小的,和刚开始假定的最小值arr[i]进行交换
            temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
        }
    }
}

2.4选择排序的时间复杂度分析

选择排序使用了双层for循环,其中外层循环完成了数据交换,内层循环完成了数据比较,所以我们分别统计数据 交换次数和数据比较次数:

数据比较次数: (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

数据交换次数: N-1

时间复杂度:N^2/2-N/2+(N-1)=N^2/2+N/2-1;

根据大O推导法则,保留最高阶项,去除常数因子,时间复杂度为O(N^2);

2.5选择排序的空间复杂度分析

空间复杂度,最优的情况下(已经有顺序)复杂度为:O(0) ;最差的情况下(全部元素都要重新排序)复杂度为:O(n );;平均的时间复杂度:O(1)

3.插入排序

3.1插入排序原理

1.将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

2.从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。

(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

3.2插入排序图解

3.3插入排序代码实现

/**
 * 插入排序,可直接运行
 */
public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {9, 6, 14, 2, 55, 12, 34, 7};
        sortByInsert(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

    private static void sortByInsert(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        //从数组下标为 1 的地方和前面的元素进行比较,开始选择合适的地方进行插入
        //因为下标为0的只有一个元素,默认是有序的
        for (int i = 1; i < arr.length; i++) {
            //把该需要比较的元素先保存一份
            int temp = arr[i];
            //从已经排序的最右边开始进行比较,找到比其小的数
            int j = i;
            while(j>0 && temp < arr[j - 1]){
                //如果比较的元素小的话,将比较的对应元素向后移动
                arr[j] = arr[j - 1];
                j--;
            }
            //如果j==i,那么排好序的元素都比需要比较的元素小,不需要插入
            //如果j!=i,那么说明有比需要比较的元素小的数,并且位置移动了,需要插入
            if (j!=i){
                arr[j] = temp;
            }
        }
    }

}

3.4插入排序的时间复杂度分析:

插入排序使用了双层for循环,其中内层循环的循环体是真正完成排序的代码,所以,我们分析插入排序的时间复 杂度,主要分析一下内层循环体的执行次数即可。

最坏情况,也就是待排序的数组元素为{55,34,14,12,9,7,6,2},那么:

比较的次数为: (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

交换的次数为: (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

总执行次数为: (N^2/2-N/2)+(N^2/2-N/2)=N^2-N;

按照大O推导法则,保留函数中的最高阶项那么最终插入排序的时间复杂度为O(N^2).

插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千;或者若已知输入元素大致上按照顺序排列,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序

==================================================

另外,如果觉得文章对你有用,下边点个赞吧!     
你们的支持,是我坚持的动力。

==================================================

猜你喜欢

转载自blog.csdn.net/i_nclude/article/details/112682852