今日打算入手學習一下算法相關的東西,就先從排序算法說起。冒泡排序(Bubble-Sort)應該是大學裏C語言課堂上學到的最早幾個排序算法之一的吧。考慮排序後數組爲升序排序的情況。冒泡排序,就是在每一次迭代循環中,從當前剩下的數組中選擇最小或者最大的值,並將其移動帶數組前面或者後面,直到整個數組呈現出升序排序。由於算法本身偏於簡單,因此這裏就沒有動態圖來做演示(主要是因爲自己不會做)。
另外,由於筆者之前在某一微信公衆號上曾經見到過有人提出過這樣的問題。
Q:冒泡排序算法有沒有什麼優化方案?
其實筆者一開始也是一臉懵逼,這種算法還有什麼可以優化的呢?在看完文章後才知道,若在某一次迭代中,發現沒有相鄰的兩個元素進行交換,那麼此時的數組已經是(嚴格)升序的了,就沒有在繼續循環下去的必要,可以提前結束迭代過程,優化算法所需時間。因此,這裏筆者給出給出冒泡排序算法實現的兩種情況,即
- 每次迭代過程中都從當前剩下的數組中選擇最小的元素並放置在數組前面
- 每次迭代過程中都從當前剩下的數組中選擇最大的元素並防止在數組後面
並且將冒泡排序進行算法上的優化。冒泡排序算法在最壞的情況下的時間複雜度O(n^2),其本身是一種穩定排序算法,具體原因請參見代碼註釋內容。
import com.sun.istack.internal.NotNull;
import java.util.Arrays;
import java.util.Random;
/**
* A demo of {@code BubbleSort}.
*
* @author Mr.K
*/
public class BubbleSort {
public static void main(String[] args) {
int N = 20;
int[] numbers = new int[N];
Random random = new Random();
for (int i = 0; i < N; i++) {
numbers[i] = random.nextInt(2 * N);
}
System.out.println("待排序數組: "+Arrays.toString(numbers) + "\n");
bubbleSort(numbers);
System.out.println("\n已排序數組: " + Arrays.toString(numbers));
}
/**
* Accepts an array and sorts this array by algorithm {@code BubbleSort}.
* <ul>
* <li>This algorithm contains two for-loop, where the first <em>For-Loop</em>
* starts from the first element to the last one and the second <em>For-Loop</em>
* starts from the last element to the i-th element where i is an index from
* outer layer's <em>For-Loop</em>.</li>
* <li>When outer layer's <em>For-Loop</em> finishes the first iteration, the
* minimum or maximum of the specified array will be moved to the head or end
* of the array and the second-minimum or second-maximum of the remaining array
* will be moved to the second behind the head or ahead the end after outer
* layer's <em>For-Loop</em> finishes the second iteration.</li>
* <li>When changing two element, one ordinary method is assigning one of both
* elements to a temporary variable and then exchanging values like this:<br>
* <blockquote>
* int temp = numbers[j];<br>
* numbers[j] = numbers[j - 1];<br>
* numbers[j - 1] = temp;
* </blockquote>
* But, in the implementation of the method, <em>Xor</em> is used to exchanged the
* value of two numbers like:
* <blockquote>
* int temp = numbers[j] ^ numbers[j - 1];<br>
* numbers[j] = temp ^ numbers[j];<br>
* numbers[j - 1] = temp ^ numbers[j - 1];
* </blockquote></li>
* <li>An improvement has been implemented in this method where a flag has been used
* to indicate whether there exists exchanges. If no exchange has been made, the flag
* will be false, thus outer layer's <em>For-Loop</em> will end and the process will
* finish.</li>
* </ul>
* Since the parameter is in a form of array, no results will be returned cause the
* specified parameter is a reference.<br><br>
* <p>
* Be aware, the cost of time of {@code BubbleSort} goes to O(n^2) in the bad cases,
* which may be a bottleneck in some cases.<br><br>
* By the way, {@code BubbleSort} is stable cause the judgement
* <blockquote>
* if (numbers[j - 1] > numbers[j])<br>
* </blockquote>
* means only when the former number is greater than the later one, exchange will be made;
* otherwise, even when the former one equals the later one, there is no exchange. So, if
* there are some numbers with the same values, there is no change in their orders. That
* is why {@code BubbleSort} is stable. if the judgement change to
* <blockquote>
* if (numbers[j - 1] >= numbers[j])<br>
* </blockquote>
* then {@code BubbleSort} will be unstable.
*
* @param numbers specified array to be sorted
*/
public static void bubbleSort(@NotNull int[] numbers) {
/**
* Copy specified array to self-defined array.
*/
int[] arr = new int[numbers.length];
System.arraycopy(numbers, 0, arr, 0, numbers.length);
/**
* This is one version of {@code BubbleSort} which selects the minimum
* number from the remaining array and moves it to the head of array.
*/
System.out.println("Sorts the array by selecting the minimum number in each iteration.");
for (int i = 0; i < numbers.length; i++) {
System.out.println("第" + String.format("%2d", i) + "步 -> "
+ Arrays.toString(numbers));
boolean isExchanged = false;
for (int j = numbers.length - 1; j > i; j--) {
if (numbers[j - 1] > numbers[j]) {
int num = numbers[j - 1] ^ numbers[j];
numbers[j] = num ^ numbers[j];
numbers[j - 1] = num ^ numbers[j - 1];
isExchanged = true;
}
}
if (!isExchanged) {
break;
}
}
/**
* This is another version of {@code BubbleSort} which selects the maximum
* number from the remaining array and moves it to the end of array.
*/
System.out.println("Sorts the array by selecting the maximum number in each iteration.");
for (int i = 0; i < arr.length; i++) {
System.out.println("第" + String.format("%2d", i) + "步 -> "
+ Arrays.toString(arr));
boolean isExchanged = false;
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j] ^ arr[j + 1];
arr[j] = temp ^ arr[j];
arr[j + 1] = temp ^ arr[j + 1];
isExchanged = true;
}
}
if (!isExchanged) {
break;
}
}
}
}
某次運行結果如下所示:
待排序數組: [28, 28, 1, 27, 28, 15, 29, 9, 23, 24, 11, 2, 14, 19, 31, 10, 20, 35, 35, 24]
Sorts the array by selecting the minimum number in each iteration.
第 0步 -> [28, 28, 1, 27, 28, 15, 29, 9, 23, 24, 11, 2, 14, 19, 31, 10, 20, 35, 35, 24]
第 1步 -> [1, 28, 28, 2, 27, 28, 15, 29, 9, 23, 24, 11, 10, 14, 19, 31, 20, 24, 35, 35]
第 2步 -> [1, 2, 28, 28, 9, 27, 28, 15, 29, 10, 23, 24, 11, 14, 19, 20, 31, 24, 35, 35]
第 3步 -> [1, 2, 9, 28, 28, 10, 27, 28, 15, 29, 11, 23, 24, 14, 19, 20, 24, 31, 35, 35]
第 4步 -> [1, 2, 9, 10, 28, 28, 11, 27, 28, 15, 29, 14, 23, 24, 19, 20, 24, 31, 35, 35]
第 5步 -> [1, 2, 9, 10, 11, 28, 28, 14, 27, 28, 15, 29, 19, 23, 24, 20, 24, 31, 35, 35]
第 6步 -> [1, 2, 9, 10, 11, 14, 28, 28, 15, 27, 28, 19, 29, 20, 23, 24, 24, 31, 35, 35]
第 7步 -> [1, 2, 9, 10, 11, 14, 15, 28, 28, 19, 27, 28, 20, 29, 23, 24, 24, 31, 35, 35]
第 8步 -> [1, 2, 9, 10, 11, 14, 15, 19, 28, 28, 20, 27, 28, 23, 29, 24, 24, 31, 35, 35]
第 9步 -> [1, 2, 9, 10, 11, 14, 15, 19, 20, 28, 28, 23, 27, 28, 24, 29, 24, 31, 35, 35]
第10步 -> [1, 2, 9, 10, 11, 14, 15, 19, 20, 23, 28, 28, 24, 27, 28, 24, 29, 31, 35, 35]
第11步 -> [1, 2, 9, 10, 11, 14, 15, 19, 20, 23, 24, 28, 28, 24, 27, 28, 29, 31, 35, 35]
第12步 -> [1, 2, 9, 10, 11, 14, 15, 19, 20, 23, 24, 24, 28, 28, 27, 28, 29, 31, 35, 35]
第13步 -> [1, 2, 9, 10, 11, 14, 15, 19, 20, 23, 24, 24, 27, 28, 28, 28, 29, 31, 35, 35]
Sorts the array by selecting the maximum number in each iteration.
第 0步 -> [28, 28, 1, 27, 28, 15, 29, 9, 23, 24, 11, 2, 14, 19, 31, 10, 20, 35, 35, 24]
第 1步 -> [28, 1, 27, 28, 15, 28, 9, 23, 24, 11, 2, 14, 19, 29, 10, 20, 31, 35, 24, 35]
第 2步 -> [1, 27, 28, 15, 28, 9, 23, 24, 11, 2, 14, 19, 28, 10, 20, 29, 31, 24, 35, 35]
第 3步 -> [1, 27, 15, 28, 9, 23, 24, 11, 2, 14, 19, 28, 10, 20, 28, 29, 24, 31, 35, 35]
第 4步 -> [1, 15, 27, 9, 23, 24, 11, 2, 14, 19, 28, 10, 20, 28, 28, 24, 29, 31, 35, 35]
第 5步 -> [1, 15, 9, 23, 24, 11, 2, 14, 19, 27, 10, 20, 28, 28, 24, 28, 29, 31, 35, 35]
第 6步 -> [1, 9, 15, 23, 11, 2, 14, 19, 24, 10, 20, 27, 28, 24, 28, 28, 29, 31, 35, 35]
第 7步 -> [1, 9, 15, 11, 2, 14, 19, 23, 10, 20, 24, 27, 24, 28, 28, 28, 29, 31, 35, 35]
第 8步 -> [1, 9, 11, 2, 14, 15, 19, 10, 20, 23, 24, 24, 27, 28, 28, 28, 29, 31, 35, 35]
第 9步 -> [1, 9, 2, 11, 14, 15, 10, 19, 20, 23, 24, 24, 27, 28, 28, 28, 29, 31, 35, 35]
第10步 -> [1, 2, 9, 11, 14, 10, 15, 19, 20, 23, 24, 24, 27, 28, 28, 28, 29, 31, 35, 35]
第11步 -> [1, 2, 9, 11, 10, 14, 15, 19, 20, 23, 24, 24, 27, 28, 28, 28, 29, 31, 35, 35]
第12步 -> [1, 2, 9, 10, 11, 14, 15, 19, 20, 23, 24, 24, 27, 28, 28, 28, 29, 31, 35, 35]
已排序數組: [1, 2, 9, 10, 11, 14, 15, 19, 20, 23, 24, 24, 27, 28, 28, 28, 29, 31, 35, 35]
根據運行結果可以發現,對於同一個數組,每一次迭代選擇最小或者最大的值並移動到相應位置所花費的步驟是不完全一致的,當然,這個是在冒泡排序本身優化後的結果。