今日打算入手学习一下算法相关的东西,就先从排序算法说起。冒泡排序(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]
根据运行结果可以发现,对于同一个数组,每一次迭代选择最小或者最大的值并移动到相应位置所花费的步骤是不完全一致的,当然,这个是在冒泡排序本身优化后的结果。