數據結構與算法學習十一:冒泡排序、選擇排序、插入排序

前言

一、冒泡排序

1.1 基本介紹

冒泡排序(Bubble Sorting)的基本思想是:通過對待排序序列從前向後(從下標較小的元素開始), 依次比較相鄰元素的值,若發現逆序則交換 ,使值較大的元素逐漸從前移向後部,就象水底下的氣泡一樣逐漸向上冒。

因爲排序的過程中,各元素不斷接近自己的位置,如果一趟比較下來沒有進行過交換,就說明序列有序 ,因此要在排序過程中設置一個標誌flag判斷元素是否進行過交換。從而減少不必要的比較。(這裏說的優化,可以在冒泡排序寫好後,在進行)

1.2 演示冒泡過程的例子(圖解)

小節下面的冒泡排序的圖解過程:

  1. 一共進行 【數組的大小-1 】大的循環
  2. 每一趟排序的次數在逐漸的減少
  3. 如果我們發現在某趟排序中,沒有發生一次交換, 可以提前結束冒泡排序。這個就是優化
    在這裏插入圖片描述

1.3 代碼實現

  • 時間複雜度:O(n^2)

  • 實現對一位數組{3, 9, -1, 10, -2}的排序

  • 代碼實現是分爲三部分學習的

  1. 對每一步的循環進行分析、推導,其方法爲 deductionBubbleSort(int[] array)
  2. 對第一步的推導找出規律,合成兩個 for 循環。其方法爲 bubbleSort(int[] array)
  3. 測試 80000 個數據排序所需要的時間。 其方法爲 testTime()方法,26S 左右
package com.feng.ch09_sort;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/*
 * 冒泡排序
 * 時間複雜度爲 O(n^2)
 * 分爲三步:
 * 1、每一步進行推導。 deductionBubbleSort(int[] array)方法
 * 2、找到規律,合成兩個for循環。 bubbleSort(int[] array)方法,並進行優化:比較後沒有交換的不進行循環一趟
 * 3、測試 80000 個數據排序所需要的時間。 testTime()方法         26S 左右
 * */
public class S1_BubbleSort {
    public static void main(String[] args) {
        int[] array = {3, 9, -1, 10, -2};

        System.out.println("排序前:");
        System.out.println(Arrays.toString(array));

        System.out.println();
        System.out.println("排序後:");
//        deductionBubbleSort(array);  // 推導的方法。
        int[] ints = bubbleSort(array); // 推導後的方法
        System.out.println(Arrays.toString(ints));

        // 測試 80000 個數據排序 所用的時間
        System.out.println();
        System.out.println("測試 80000 個數據 採用冒泡排序 所用的時間:");
        testTime();
    }

    /*
     * 測試一下 冒泡排序的速度O(n^2), 給 80000 個數據,測試一下
     * */
    public static void testTime() {
        // 創建一個 80000個的隨機的數組
        int array2[] = new int[80000];
        for (int i = 0; i < 80000; i++) {
            array2[i] = (int) (Math.random() * 8000000); // 生成一個[ 0, 8000000] 數
        }
//        System.out.println(Arrays.toString(array2)); // 不在打印,耗費時間太長


        long start = System.currentTimeMillis();  //返回以毫秒爲單位的當前時間
        System.out.println("long start:" + start);
        Date date = new Date(start); // 上面的也可以不要,但是我想測試
        System.out.println("date:" + date);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        System.out.println("排序前的時間是=" + format.format(date));

        bubbleSort(array2);

        System.out.println();
        long end = System.currentTimeMillis();
        Date date2 = new Date(end); // 上面的也可以不要,但是我想測試
        System.out.println("排序後的時間是=" + format.format(date2));
        System.out.println("共耗時" + (end - start) + "毫秒");
        System.out.println("毫秒轉成秒爲:" + ((end - start) / 1000) + "秒");
    }

    /*
     * 推導後,合成的方法【重點掌握】
     * */
    public static int[] bubbleSort(int[] array) {
        int temporary = 0; // 臨時變量
        boolean flag = false;
        for (int i = 0; i < array.length - 1; i++) { // 總共爲 4 趟 大遍歷。
            for (int j = 0; j < array.length - 1 - i; j++) {
                // 如果前面的數比後面的數大,則交換
                if (array[j] > array[j + 1]) {
                    flag = true;
                    temporary = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temporary;
                }
            }
//            System.out.println("第" + (i + 1) + "躺排序後的數組");
//            System.out.println(Arrays.toString(array));
            if (!flag) { // 在一趟排序中,一次交換都沒有發生過
                break;
            } else {
                flag = false; // 重置flag!!!, 進行下次判斷
            }
        }
        return array;
    }

    /*
     * 推導的方法
     * */
    public static int[] deductionBubbleSort(int[] array) {
        /*
         * 爲了容易理解,把冒泡排序的演變過程展示一下:
         * */
        //1、第一趟排序,就是將最大的數排在最後
        int temporary = 0; // 臨時變量
        for (int i = 0; i < array.length - 1 - 0; i++) { // 總共爲 4次 大循環遍歷
            // 如果前面的數比後面的數大,則交換
            if (array[i] > array[i + 1]) {
                temporary = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temporary;
            }
        }

        System.out.println("每步演練的過程::");
        System.out.println("第一趟排序後的數組:");
        System.out.println(Arrays.toString(array));

        // 第二趟排序,就在將第二大的數排在倒數第二位
        for (int i = 0; i < array.length - 1 - 1; i++) { // 總共爲 4次 大循環遍歷
            // 如果前面的數比後面的數大,則交換
            if (array[i] > array[i + 1]) {
                temporary = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temporary;
            }
        }
        System.out.println("第二趟排序後的數組:");
        System.out.println(Arrays.toString(array));

        // 第三趟排序,就在將第二大的數排在倒數第三位
        for (int i = 0; i < array.length - 1 - 2; i++) { // 總共爲 4次 大循環遍歷
            // 如果前面的數比後面的數大,則交換
            if (array[i] > array[i + 1]) {
                temporary = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temporary;
            }
        }
        System.out.println("第三趟排序後的數組:");
        System.out.println(Arrays.toString(array));

        // 第四趟排序,就在將第二大的數排在倒數第四位
        for (int i = 0; i < array.length - 1 - 3; i++) { // 總共爲 4次 大循環遍歷
            // 如果前面的數比後面的數大,則交換
            if (array[i] > array[i + 1]) {
                temporary = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temporary;
            }
        }
        System.out.println("第四趟排序後的數組:");
        System.out.println(Arrays.toString(array));

        return array;
    }


}

1.4 測試結果

在這裏插入圖片描述

二、選擇排序

2.1 基本介紹

選擇式排序也屬於 內部排序法,是從欲排序的數據中,按指定的規則選出某一元素,再依規定交換位置後達到排序的目的。

2.2 思路分析

2.2.1 選擇排序思想

選擇排序(select sorting)也是一種簡單的排序方法。它的 基本思想是
第一次從arr[0]~ arr[n-1]中選取最小值,與arr[0]交換,
第二次從arr[1]~ arr[n-1]中選取最小值,與arr[1]交換,
第三次從arr[2]~ arr[n-1]中選取最小值,與arr[2]交換,…,
第i次從arr[i-1]~ arr[n-1]中選取最小值,與arr[i-1]交換,…,
第n-1次從arr[n-2]~arr[n-1]中選取最小值,與arr[n-2]交換,
總共通過n-1次,得到一個按排序碼從小到大排列的有序序列。

2.2.2 思路分析圖

對一個數組的選擇排序再進行講解
在這裏插入圖片描述
對圖解的說明:
說明:

  1. 選擇排序一共有 【數組大小 - 1】 輪排序
  2. 每1輪排序,又是一個循環, 循環的規則(代碼)
    2.1 先假定 當前這個數是最小數
    2.2 然後和後面的每個數進行比較,如果發現有比當前數更小的數,就重新確定最小數,並得到下標
    2.3 當遍歷到數組的最後時,就得到本輪最小數和下標
    2.4 交換 [代碼中再繼續說 ]

在這裏插入圖片描述

2.3 代碼實現

  • 時間複雜度:O(n^2)

  • 實現對一位數組{101, 34, 119, 1}的排序

  • 代碼實現是分爲三部分學習的

  1. 對每一步的循環進行分析、推導,其方法爲 deductionSelectSort(int[] array)
    2.1 首先定義兩個變量,minIndex、 min,分別表示 最小值下標、最小值
    2.2 假定第一個數據爲最小值,然後拿着這個數據和其他數據比較(使用遍歷)
    2.3 找到最小值後,將其與數組的第一個值進行互換(默認排序是從小到大)
  2. 對第一步的推導找出規律,合成兩個 for 循環。其方法爲 selectSort(int[] array)
  3. 測試 80000 個數據排序所需要的時間。 其方法爲 testTime()方法,5S 左右, 比冒泡快
package com.feng.ch09_sort;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/*
 * 選擇排序
 * 時間複雜度: O(n^2)
 * 分爲三步:
 * 1、每一步進行推導。 deductionSelectSort(int[] array)方法
 *    1.1 首先定義兩個變量,minIndex、 min,分別表示 最小值下標、最小值
 *    2.1 假定第一個數據爲最小值,然後拿着這個數據和其他數據比較(使用遍歷)
 *    1.2 找到最小值後,將其與數組的第一個值進行互換(默認排序是從小到大)
 * 2、找到規律,合成兩個for循環。 selectSort(int[] array)方法,並進行優化:
 * 3、測試 80000 個數據排序所需要的時間。 testTime()方法         5S 左右, 比冒泡快
 * */
public class S2_SelectSort {
    public static void main(String[] args) {
        int array[] = new int[]{101, 34, 119, 1};
        System.out.println("排序前:");
        System.out.println(Arrays.toString(array));

        System.out.println();
        System.out.println("排序後:");
//        deductionSelectSort(array); // 推導 的過程
        int[] ints = selectSort(array); // 推導後的方法
        System.out.println(Arrays.toString(ints));

        // 測試 80000 個數據排序 所用的時間
        System.out.println();
        System.out.println("測試 80000 個數據 採用選擇排序 所用的時間:");
        testTime();
    }

    /*
     * 測試一下 冒泡排序的速度O(n^2), 給 80000 個數據,測試一下
     * */
    public static void testTime() {
        // 創建一個 80000個的隨機的數組
        System.out.println();
        int array2[] = new int[80000];
        for (int i = 0; i < 80000; i++) {
            array2[i] = (int) (Math.random() * 8000000); // 生成一個[ 0, 8000000] 數
        }
//        System.out.println(Arrays.toString(array2)); // 不在打印,耗費時間太長


        long start = System.currentTimeMillis();  //返回以毫秒爲單位的當前時間
        System.out.println("long start:" + start);
        Date date = new Date(start); // 上面的也可以不要,但是我想測試
        System.out.println("date:" + date);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        System.out.println("排序前的時間是=" + format.format(date));

        selectSort(array2);

        System.out.println();
        long end = System.currentTimeMillis();
        Date date2 = new Date(end); // 上面的也可以不要,但是我想測試
        System.out.println("排序後的時間是=" + format.format(date2));
        System.out.println("共耗時" + (end - start) + "毫秒");
        System.out.println("毫秒轉成秒爲:" + ((end - start) / 1000) + "秒");
    }

    /*
     * 推導後,合成的方法【重點掌握】
     * */
    public static int[] selectSort(int[] array) {
        /*
         * 在 deductionSelectSort()方法 推導的過程,發現了規律,因此,可以使用for循環解決。
         * 4個數據, 每次循環比較,找到最小值,放到最前面。共需要 3 次。  而每次找的時候 需要比較循環 3 次。正好爲 【數據的大小-1】
         * */
        for (int i = 0; i < array.length - 1; i++) {
            int minIndex = i;
            int min = array[i];
            for (int j = i + 1; j < array.length; j++) {
                if (min > array[j]) {  // 說明假定的最小值,並不是最小
                    minIndex = j; // 重置 min
                    min = array[j]; // 重置 minIndex
                }
            }
            // 將最小值,放在 array[i], 即交換: 交換時 做一個判斷,如果 比較後,不需要賦值,那麼下面兩行不在執行,否則就重複了
            if (minIndex != i) {
                array[minIndex] = array[i];
                array[i] = min;
            }
//            System.out.println("第一輪後:");
//            System.out.println(Arrays.toString(array));
        }
        return array;
    }

    /*
     * 推導的方法
     * */
    public static void deductionSelectSort(int[] array) {
        /*
         * 使用逐步推導的方式來,講解選擇排序
         * 第1輪
         * 原始的數組;101, 34, 119, 1
         * 第一輪排序:[1, 34, 119, 101]
         * 算法 先簡單--》做複雜,就是可以把一個複雜的算法,拆分成簡單的問題-》逐步解決
         * */

        // 第一輪排序後:
        int minIndex = 0;
        int min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (min > array[i]) {  // 說明假定的最小值,並不是最小
                minIndex = i; // 重置 min
                min = array[i]; // 重置 minIndex
            }
        }
        // 將最小值,放在 array[0], 即交換: 交換時 做一個判斷,如果 比較後,不需要賦值,那麼下面兩行不在執行,否則就重複了
        if (minIndex != 0) {
            array[minIndex] = array[0];
            array[0] = min;
        }
        System.out.println("第一輪後:");
        System.out.println(Arrays.toString(array));


        // 第二輪排序後:
        minIndex = 1;
        min = array[1];
        for (int i = 1 + 1; i < array.length; i++) {
            if (min > array[i]) {  // 說明假定的最小值,並不是最小
                minIndex = i; // 重置 min
                min = array[i]; // 重置 minIndex
            }
        }
        // 將最小值,放在 array[0], 即交換: 交換時 做一個判斷,如果 比較後,不需要賦值,那麼下面兩行不在執行,否則就重複了
        if (minIndex != 1) {
            array[minIndex] = array[1];
            array[1] = min;
        }

        System.out.println("第二輪後:");
        System.out.println(Arrays.toString(array));

        // 第三輪排序後:
        minIndex = 2;
        min = array[2];
        for (int i = 2 + 1; i < array.length; i++) {
            if (min > array[i]) {  // 說明假定的最小值,並不是最小
                minIndex = i; // 重置 min
                min = array[i]; // 重置 minIndex
            }
        }
        // 將最小值,放在 array[0], 即交換: 交換時 做一個判斷,如果 比較後,不需要賦值,那麼下面兩行不在執行,否則就重複了
        array[minIndex] = array[2];
        array[2] = min;
        System.out.println("第三輪後:");
        System.out.println(Arrays.toString(array));
    }
}

2.4 測試結果

在這裏插入圖片描述

三、插入排序

3.1 基本介紹

插入式排序屬於內部排序法,是對於欲排序的元素以插入的方式找尋該元素的適當位置,以達到排序的目的。

3.2 思路分析

3.2.1 插入排序思想

插入排序(Insertion Sorting)的基本思想是
把n個待排序的元素看成爲一個有序表和一個無序表,開始時有序表中只包含一個元素,無序表中包含有n-1個元素,排序過程中每次從無序表中取出第一個元素,把它的排序碼依次與有序表元素的排序碼進行比較,將它插入到有序表中的適當位置,使之成爲新的有序表。

3.2.2 插入排序思路圖

在這裏插入圖片描述

3.3 代碼實現

  • 時間複雜度:O(n^2)

  • 實現對一位數組{101, 34, 20, 1}的排序

  • 代碼實現是分爲三部分學習的

  1. 對每一步的循環進行分析、推導,其方法爲 deductionSelectSort(int[] array)
    2.1 先定義兩個變量:insertValue、insertIndex,分別代表 待插入的值(從數組第二個值開始)、待插入的下標
    2.2 先保存數組第二個值,進行循環,每次循環判斷第二值(當前值)與第一個值(前一個值)的大小,若小則將第一個值(前一個值)後移,
    2.3 進行循環,循環一次,便將 保存的當前值插入到所應該在的前面的位置。
  2. 對第一步的推導找出規律,合成兩個 for 循環。其方法爲 selectSort(int[] array)
  3. 測試 80000 個數據排序所需要的時間。 其方法爲 testTime()方法,5S 左右, 比冒泡快
package com.feng.ch09_sort;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/*
 * 插入排序
 * 時間複雜度: O(n^2)
 * 分爲三步:
 * 1、每一步進行推導。 deductionInsertSort(int[] array)方法
 *      1.1 先定義兩個變量:insertValue、insertIndex,分別代表 待插入的值(從數組第二個值開始)、待插入的下標
 *      1.2 先保存數組第二個值,進行循環,每次循環判斷第二值(當前值)與第一個值(前一個值)的大小,若小則將第一個值(前一個值)後移,
 *      1.3 進行循環,循環一次,便將 保存的當前值插入到所應該在的前面的位置。
 * 2、找到規律,合成兩個for循環。 insertSort(int[] array)方法,並進行優化,優化 重點,二行代碼
 * 3、測試 80000 個數據排序所需要的時間。 testTime()方法      1S 左右, 比選擇、冒泡快
 * */
public class S3_InsertSort {

    public static void main(String[] args) {
        int[] array = {101, 34, 20, 1};

        System.out.println("原始數組:");
        System.out.println(Arrays.toString(array));

        System.out.println();
        deductionInsertSort(array);
//        int[] ints = insertSort(array);

        System.out.println("排序後:");
//        System.out.println(Arrays.toString(ints));

        // 測試 80000 個數據排序 所用的時間
        System.out.println();
        System.out.println("測試 80000 個數據 採用插入排序 所用的時間:");
        //testTime();
    }

    /*
     * 測試一下 冒泡排序的速度O(n^2), 給 80000 個數據,測試一下
     * */
    public static void testTime() {
        // 創建一個 80000個的隨機的數組
        System.out.println();
        int array2[] = new int[80000];
        for (int i = 0; i < 80000; i++) {
            array2[i] = (int) (Math.random() * 8000000); // 生成一個[ 0, 8000000] 數
        }
//        System.out.println(Arrays.toString(array2)); // 不在打印,耗費時間太長


        long start = System.currentTimeMillis();  //返回以毫秒爲單位的當前時間
        System.out.println("long start:" + start);
        Date date = new Date(start); // 上面的也可以不要,但是我想測試
        System.out.println("date:" + date);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        System.out.println("排序前的時間是=" + format.format(date));

        insertSort(array2);

        System.out.println();
        long end = System.currentTimeMillis();
        Date date2 = new Date(end); // 上面的也可以不要,但是我想測試
        System.out.println("排序後的時間是=" + format.format(date2));
        System.out.println("共耗時" + (end - start) + "毫秒");
        System.out.println("毫秒轉成秒爲:" + ((end - start) / 1000) + "秒");
    }

    /*
     * 推導後 合成的 插入排序
     * */
    public static int[] insertSort(int[] array) {
        int insertValue = 0;
        int insertIndex = 0;
        for (int i = 1; i < array.length; i++) {
            // 定義 待插入 的數
            insertValue = array[i]; // 保存 第二個數
            insertIndex = i - 1;   // 保存要插入的位置
            /*
             * 給 insertValue 找到插入的位置
             * 說明
             * 1、insertIndex >= 0 保證再給 insertValue 找插入位置,不越界
             * 2、insertValue < array[insertIndex] 待插入的數,還沒有找到插入位置
             * 3、就需要 array[insertIndex] 後移
             * */
            while (insertIndex >= 0 && insertValue < array[insertIndex]) {
                array[insertIndex + 1] = array[insertIndex];
                insertIndex--;
            }
            // 當退出 while 循環時,說明插入的位置找到,insertIndex +1;
            // 舉例:理解不了,debug 可以幫助理解
            // 這裏我們判斷是否需要賦值
            if (insertIndex + 1 != i) { // insertIndex+1 == i  ,就不需要進行賦值了,因爲這時是正好的排序
                array[insertIndex + 1] = insertValue;
            }
        }
        return array;
    }

    /*
     * 推導插入排序
     * */
    public static int[] deductionInsertSort(int[] array) {
        // 使用逐步推導的方式來講解,便於理解

        // 第一輪{101, 34, 119, 1} =》 {34, 101 119, 1}
        // 定義 待插入 的數、插入的位置
        int insertValue = array[1]; // 保存 第二個數
        int insertIndex = 1 - 1;   // 保存要插入的位置

        /*
         * 給 insertValue 找到插入的位置
         * 說明
         * 1、insertIndex >= 0 保證再給 insertValue 找插入位置,不越界
         * 2、insertValue < array[insertIndex] 待插入的數,還沒有找到插入位置
         * 3、就需要 array[insertIndex] 後移
         * */
        while (insertIndex >= 0 && insertValue < array[insertIndex]) {
            array[insertIndex + 1] = array[insertIndex];
            insertIndex--;
        }
        // 當退出 while 循環時,說明插入的位置找到,insertIndex +1;
        // 舉例:理解不了,debug 可以幫助理解
        array[insertIndex + 1] = insertValue;

        System.out.println("第一輪插入");
        System.out.println(Arrays.toString(array));


        // 第 2 輪
        insertValue = array[2];
        insertIndex = 2 - 1;
        while (insertIndex >= 0 && insertValue < array[insertIndex]) {
            array[insertIndex + 1] = array[insertIndex];
            insertIndex--;
        }
        array[insertIndex + 1] = insertValue;
        System.out.println("第二輪插入");
        System.out.println(Arrays.toString(array));

        // 第 3 輪
        insertValue = array[3];
        insertIndex = 3 - 1;
        while (insertIndex >= 0 && insertValue < array[insertIndex]) {
            array[insertIndex + 1] = array[insertIndex];
            insertIndex--;
        }
        array[insertIndex + 1] = insertValue;
        System.out.println("第三輪插入");
        System.out.println(Arrays.toString(array));

        return array;
    }
}

3.4 測試結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章