分治策略對數組進行排序(二分排序算法)
今天我來熟悉鞏固一下分治算法對數組進行排序,分治問題就是把複雜的大問題拆解成簡單的小問題,再將小問題依次解決,假設我們要對下面這個數組進行排序輸出:
[ 32, 11, 22, 17, 222, 42, 162, 82, 1, 1231, 12, 2 ]
如果使用分治策略的話,我們需要先將大問題分解成小問題,我們可以先將長度 12 的數組分解爲兩個長度爲 6 的數組,分解後如下
[ 32, 11, 22, 17, 222, 42 ]
[ 162, 82, 1, 1231, 12, 2 ]
分解過後是否可以直接進行排序了呢?好像還是不太行,那麼我們可以繼續分解,分解到可以直接進行比較爲止
// 第二次分解
[ 32, 11, 22 ]
[ 17, 222, 42 ]
[ 162, 82, 1 ]
[ 1231, 12, 2 ]
// 第三次分解
[ 32, 11 ]
[ 22 ]
[ 17, 222 ]
[ 42 ]
[ 162, 82 ]
[ 1 ]
[ 1231, 12 ]
[ 2 ]
// 第四次分解
[ 32 ]
[ 11 ]
[ 22 ]
[ 17 ]
[ 222 ]
[ 42 ]
[ 162 ]
[ 82 ]
[ 1 ]
[ 1231 ]
[ 12 ]
[ 2 ]
我們的最終目標是將複雜數組分解成爲長度爲 1 的小數組,這樣我們就可以直接對兩個數組進行比較,比如 [32]
與 [11]
比較,此時應該進入一個循環,比較兩個數組的首個元素,比較完成後將其合併得到 [ 11, 32 ]
,再使用 [ 11, 32 ]
與 [ 22 ]
比較數組的首個元素大小,比較完成後進行合併得到 [ 11, 22, 32 ]
,以此類推,最後得到的將會是一個排序完成的。
我們將上面的分析進行步驟劃分,我們應該需要實現兩個功能:
- 將複雜數組劃分爲原子級別的簡單數組;
- 將原子級別的數組進行比對後依次合併,重新組成排序好的複雜數組;
我們現在來進行代碼的實現,首先我們創建一個接口來形容這個排序算法類
public interface Sort {
public int[] sort(int[] arr);
}
然後我們來編輯這個類的實現,最後的調用效果應該是這樣的:
import java.util.Arrays;
public class DivisionSort implements Sort {
@Override
public int[] sort(int[] arr) {
//...
return arr;
}
public static void main(String[] args) {
int[] arr = new int[] { 32, 11, 22, 17, 222, 42, 162, 82, 1, 1231, 12, 2 };
Sort divisionSort = new DivisionSort();
int[] sortedArr = divisionSort.sort(arr);
System.out.println(Arrays.toString(sortedArr));
}
}
我們現在先進行第一步,將複雜數組分解爲簡單數組:
private void splitArr(int[] arr, int start, int end) {
// 當數組開始位置等於結束位置才停止分割(此時數組長度爲 1)
if (end != start) {
// 取中點位置,遞歸調用將數組分爲兩份
int mid = (end + start) / 2;
splitArr(arr, start, mid);
splitArr(arr, mid + 1, end);
}
}
分解的函數就是一個遞歸調用,遞歸將數組分割爲原子級別,而合併的函數,我們可以好好想一下,如何對比 [ 11, 32 ]
和 [ 22 ]
,應該依次取出兩個數組的第一個元素,比對完成後將較小的一個推入合併後的數組。
所以這裏應該是先比較 11 和 22 的大小,然後將 11 推入合併後的數組,比較完成後再比較 32 和 22,然後將 22 推入合併後的數組,最後的比較將會判斷到某個數組已經無元素可對比,那麼另一個數組剩餘的數字均是大數,直接推入合併後的數組即可,那麼實現出來以後就是:
private void mergeArr(int[] arr, int start, int end) {
// 將數組分爲左右兩邊進行對比
// 比如 { 11 32 22 } 將被分爲 { 11 32 } 和 { 22 } 兩個數組
int mid = (start + end) / 2;
int[] leftArr = Arrays.copyOfRange(arr, start, mid + 1);
int[] rightArr = Arrays.copyOfRange(arr, mid + 1, end + 1);
int i = 0, j = 0;
for (int k = start; k <= end; k++) {
// 判斷數組是否已經沒有多餘元素
if (i == leftArr.length) {
arr[k] = rightArr[j];
j++;
continue;
}
if (j == rightArr.length) {
arr[k] = leftArr[i];
i++;
continue;
}
if (leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
} else {
arr[k] = rightArr[j];
j++;
}
}
}
最後組成後的 DivisionSort
類就是這個樣子的:
import java.util.Arrays;
public class DivisionSort implements Sort {
@Override
public int[] sort(int[] arr) {
this.splitArr(arr, 0, arr.length - 1);
return arr;
}
private void splitArr(int[] arr, int start, int end) {
if (end != start) {
int mid = (end + start) / 2;
splitArr(arr, start, mid);
splitArr(arr, mid + 1, end);
mergeArr(arr, start, end);
}
}
private void mergeArr(int[] arr, int start, int end) {
int mid = (start + end) / 2;
int[] leftArr = Arrays.copyOfRange(arr, start, mid + 1);
int[] rightArr = Arrays.copyOfRange(arr, mid + 1, end + 1);
int i = 0, j = 0;
for (int k = start; k <= end; k++) {
if (i == leftArr.length) {
arr[k] = rightArr[j];
j++;
continue;
}
if (j == rightArr.length) {
arr[k] = leftArr[i];
i++;
continue;
}
if (leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
} else {
arr[k] = rightArr[j];
j++;
}
}
}
public static void main(String[] args) {
int[] arr = new int[] { 32, 11, 22, 17, 222, 42, 162, 82, 1, 1231, 12, 2 };
Sort divisionSort = new DivisionSort();
int[] sortedArr = divisionSort.sort(arr);
System.out.println(Arrays.toString(sortedArr));
}
}
最後輸出的結果爲 [1, 2, 11, 12, 17, 22, 32, 42, 82, 162, 222, 1231]
,排序完成。