冒泡、選擇、插入、希爾排序
今天學習了八大算法中的冒泡、選擇、插入、希爾排序算法。以數據從小到大排序爲例,談一談自己的理解。有錯誤之處,還望指出,我會及時改正。
1. 冒泡排序:
冒泡排序每次將相鄰元素進行比較,較大的數放在後面,每輪下來,最大的數都會被交換到最後,並且下一輪不需要進行排序了。通過雙重for循環實現,平均和最差時間複雜度爲O(n^2)。
package com.sort.bubbleSort;
import java.util.Arrays;
/*
* 冒泡排序
*/
public class BubbleSortDemo2 {
public static void main(String[] args) {
int[] array = { 5, 9, 8, 3, 2, 0, 1 };
System.out.println("排序前:" + Arrays.toString(array));
bubbleSort(array);
System.out.println("排序後:" + Arrays.toString(array));
}
// 冒泡排序
public static void bubbleSort(int[] array) {
int temp = 0;
boolean flag = false;
// 共執行array.length-1
for (int i = 0; i < array.length - 1; i++) {
// 每輪都會將最大的數放到最後,所以每輪只需要執行array.length-1-i次
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
flag = true;
temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
// 每輪排序完,檢查一下該輪是否有數調整位置了
if (!flag) {// 如果沒有數調整位置,說明已經有序了
break;
} else {
flag = false;
}
}
}
}
2. 選擇排序
每輪將最小的數放在前面,直至放完。平均和最差時間複雜度爲O(n^2)。
package com.sort.selectionSort;
import java.util.Arrays;
/*
* 選擇排序
*/
public class SelectSortDemo2 {
public static void main(String[] args) {
int[] array = { 5, 9, 8, 3, 2, 0, 1 };
System.out.println("排序前:" + Arrays.toString(array));
selectSort(array);
System.out.println("排序後:" + Arrays.toString(array));
}
// 選擇排序
public static void selectSort(int[] array) {
int min = 0;// 記錄最小值
int minIndex = 0;// 記錄最小值的下標
// 一共需要排序array.length-1輪
for (int i = 0; i < array.length - 1; i++) {
// 假定每輪的第一個數就是最小值
min = array[i];
minIndex = i;
// 每輪會將最小的值放在最前面,所以每輪從第i+1個數開始
for (int j = i + 1; j < array.length; j++) {
if (min > array[j]) {// 說明假定的最小值並不是真正的最小值
// 將更小的那個值標記爲最小值
min = array[j];
minIndex = j;
}
}
// 每輪找到最小值後,將最小值和開始假定的最小值交換位置
if (minIndex != i) {
array[minIndex] = array[i];
array[i] = min;
}
}
}
}
3. 插入排序
插入排序的思想是將一堆數分成有序表和無序表兩類,每輪從無序表中取一個數按順序放到有序表中,直至放完。平均和最差時間複雜度爲O(n^2)。
package com.sort.insertSort;
import java.util.Arrays;
/*
* 插入排序
*/
public class InsertSortDemo2 {
public static void main(String[] args) {
int[] array = { 5, 9, 8, 3, 2, 0, 1 };
System.out.println("排序前:" + Arrays.toString(array));
insertSort(array);
System.out.println("排序後:" + Arrays.toString(array));
}
// 插入排序
/*
* 思路分析:
* 1.插入排序的思想是將一堆數分成有序和無序兩部分,每次從無序組的中取一個數核有序組進行比較,放入有序組
* 2.將array數組的第一個數看作一個有序組,後面的n-1個數看作無序組;
* 3.從第2個數開始,每取一個數就按順序插入到有序組中
* 4.記錄每次待插入的數,記錄每次待插入數前面的那個數的下標
* 5.以從小到大排序爲例,那麼每次待插入數的前一個數,即有序組的最後一個數,這個數肯定是最大的,
* 如果待插入的數小於這個最大數,說明位置沒找到插入位置,就需要跟最大數前面那個數進行比較,以此類推(下標往前移動)
* 直到待插入的數比有序組中某個數大時,插入位置就找到了,就是那個數的位置
* 6.交換位置即可
* 舉例:{101, 34, 119, 1,-1,89}==>34 {101, 101, 119, 1,-1,89}==>{34, 101, 119, 1,-1,89}
* {34, 101, 119, 1,-1,89}==>119 {34, 101, 119, 1,-1,89}==>{34, 101, 119, 1,-1,89}
* {34, 101, 119, 1,-1,89}==>1 {34, 101, 119, 119,-1,89}==>{34, 101, 101, 119,-1,89}==>{34, 34, 101, 119,-1,89}==>{1, 34, 101, 119,-1,89}
*/
public static void insertSort(int[] array) {
// 先將array中的第一個元素設爲有序表,後面n-1個元素爲無序表,接下來每次從無序表中取一個數據插入到有序表中,所以需要array.length-1輪
int insertVal = 0;// 待插入的數
int insertIndex = 0;// 待插入的位置
for (int i = 1; i < array.length; i++) {
// 每次從無序表中取第一個數作爲待插入數
insertVal = array[i];
// 將待插入數的前一個位置記錄下來,也就是有序表中最大數的位置
insertIndex = i - 1;
// 從有序表中最大數的位置開始找,如果待插入的數小於這個最大數,就找打插入位置了,否則,往前移動(將這個最大的數賦值給後一個數),直到遍歷到有序表中的第一個數,也就是最小的那個數爲止
while (insertIndex >= 0 && insertVal < array[insertIndex]) {
array[insertIndex + 1] = array[insertIndex];
insertIndex--;
}
// 遍歷結束,說明找到待插入數的位置了
if (insertIndex + 1 != i) {// 待插入位置就是自己目前的位置就不用移動(裏面就一行代碼,對性能幾乎沒有提升,也可以不做判斷)
array[insertIndex + 1] = insertVal;
}
System.out.println("第" + i + "輪排序" + Arrays.toString(array));
}
}
}
4. 希爾排序
希爾排序是一種改進的插入排序。當較小的數比較靠後時,如果使用直接插入排序會移動很多數,這樣導致性能很低,希爾排序通過設定步長進行分組排序的方式,提高了較小數靠後時排序的性能。其平均時間複雜度爲O(nlogn),最壞時間複雜度爲O(n^2)。其方式有兩種,一是交換法,二是移動法。
package com.sort.shellSort;
import java.util.Arrays;
/*
* 希爾排序
*/
public class ShellSortDemo2 {
public static void main(String[] args) {
int[] array = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
System.out.println("排序前:" + Arrays.toString(array));
shellSort2(array);
System.out.println("排序後:" + Arrays.toString(array));
}
// 希爾排序(交換法)---不提倡,交換數據對性能有影響
public static void shellSort1(int[] array) {
int temp = 0;
int count = 0;
// 計算每輪的步長,,每輪的步長也是每輪的組數
for (int gap = array.length / 2; gap > 0; gap /= 2) {
// 每輪有gap組,每組的第一個數作爲有序表,不參與排序,所以每輪需要排 array.length-gap次
for (int i = gap; i < array.length; i++) {
// 每組的數間隔爲gap
for (int j = i - gap; j >= 0; j -= gap) {
if (array[j] > array[j + gap]) {
temp = array[j + gap];
array[j + gap] = array[j];
array[j] = temp;
}
}
}
System.out.println("第" + (++count) + "輪排序:" + Arrays.toString(array));
}
}
// 希爾排序(移動法)---推薦
public static void shellSort2(int[] array) {
int count = 0;
int insertVal = 0;// 待插入的數
int insertIndex = 0;// 待插入數的前gap個數的下標
// 計算每輪的步長,,每輪的步長也是每輪的組數
for (int gap = array.length / 2; gap > 0; gap /= 2) {
// 每輪有gap組,每組的第一個數作爲有序表,不參與排序,所以每輪需要排 array.length-gap次
for (int i = gap; i < array.length; i++) {
// 先記錄待插入的數
insertVal = array[i];
// 記錄待插入數的前gap個數的下標
insertIndex = i - gap;
// 對比插入排序:如果前gap個數的下標不在數組的最前面,並且待插入的數小於前gap個數,那就將前gap個數的值賦給當前下標往前移動gap個之後,再比較
while (insertIndex >= 0 && insertVal < array[insertIndex]) {
array[insertIndex + gap] = array[insertIndex];
insertIndex -= gap;
}
// 循環結束表示找到插入位置了,就將待插入的數插入到插入位置即可
if (insertIndex + gap != i) {
array[insertIndex + gap] = insertVal;
}
}
System.out.println("第" + (++count) + "輪排序:" + Arrays.toString(array));
}
}
}