文章目錄
前言
一、冒泡排序
1.1 基本介紹
冒泡排序(Bubble Sorting)的基本思想是:通過對待排序序列從前向後(從下標較小的元素開始), 依次比較相鄰元素的值,若發現逆序則交換
,使值較大的元素逐漸從前移向後部,就象水底下的氣泡一樣逐漸向上冒。
因爲排序的過程中,各元素不斷接近自己的位置,如果一趟比較下來沒有進行過交換,就說明序列有序
,因此要在排序過程中設置一個標誌flag判斷元素是否進行過交換。從而減少不必要的比較。(這裏說的優化,可以在冒泡排序寫好後,在進行)
1.2 演示冒泡過程的例子(圖解)
小節下面的冒泡排序的圖解過程:
- 一共進行
【數組的大小-1 】
次 大的循環 - 每一趟排序的次數在逐漸的減少
- 如果我們發現在某趟排序中,沒有發生一次交換, 可以提前結束冒泡排序。這個就是優化
1.3 代碼實現
-
時間複雜度:O(n^2)
-
實現對一位數組{3, 9, -1, 10, -2}的排序
-
代碼實現是分爲三部分學習的
- 對每一步的循環進行分析、推導,其方法爲
deductionBubbleSort(int[] array)
- 對第一步的推導找出規律,合成兩個 for 循環。其方法爲
bubbleSort(int[] array)
- 測試 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.2 然後和後面的每個數進行比較,如果發現有比當前數更小的數,就重新確定最小數,並得到下標
2.3 當遍歷到數組的最後時,就得到本輪最小數和下標
2.4 交換 [代碼中再繼續說 ]
2.3 代碼實現
-
時間複雜度:O(n^2)
-
實現對一位數組{101, 34, 119, 1}的排序
-
代碼實現是分爲三部分學習的
- 對每一步的循環進行分析、推導,其方法爲
deductionSelectSort(int[] array)
2.1 首先定義兩個變量,minIndex、 min,分別表示 最小值下標、最小值
2.2 假定第一個數據爲最小值,然後拿着這個數據和其他數據比較(使用遍歷)
2.3 找到最小值後,將其與數組的第一個值進行互換(默認排序是從小到大) - 對第一步的推導找出規律,合成兩個 for 循環。其方法爲
selectSort(int[] array)
- 測試 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}的排序
-
代碼實現是分爲三部分學習的
- 對每一步的循環進行分析、推導,其方法爲
deductionSelectSort(int[] array)
2.1 先定義兩個變量:insertValue、insertIndex,分別代表 待插入的值(從數組第二個值開始)、待插入的下標
2.2 先保存數組第二個值,進行循環,每次循環判斷第二值(當前值)與第一個值(前一個值)的大小,若小則將第一個值(前一個值)後移,
2.3 進行循環,循環一次,便將 保存的當前值插入到所應該在的前面的位置。 - 對第一步的推導找出規律,合成兩個 for 循環。其方法爲
selectSort(int[] array)
- 測試 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;
}
}