设数组中部分元素已按自然数顺序排放,例如,数组 a = { 4 , 9 , 2 , 6 , 1 , 5 , 7 , 3 },则初期自然排好序的子数组段显然有4段,分别为{ 4 , 9 },{ 2 , 6 },{ 1 , 5 , 7 }和{ 3 }。请充分利用上述特点设计并实现一个自然合并排序算法。
算法设计思路
只是分割的方法不一样,这个需检测数组前一位是否比后一位大,如果大的话就分割,逐渐划分开来,他里面是有序的。只需要用一个数组来记录分割后每段数组尾数的位置,然后将数值与这个记录数组进行连接即可。然后排序方法是与合并排序相符合的。
算法实现的伪代码
算法fenge(int arr[], int n, int barr[])
输入:数组、数组长度、记录位置的数组
输出:返回数组分段的段数
- len ← 1
- barr [ 0 ] ← 0
- for i ← 1 to n
- do if arr [ i ] < arr [ i - 1 ]
-
-
-
- then barr [ len ++ ] ← i
-
-
-
- return len
算法mergeSort(int arr[], int n, int barr[], int len , int left , int right)
输入:数组、数组长度、记录位置的数组、分段段数、记录位置数组的首位下标、记录位置数组的尾部下标
输出:排序好的数组
1. if right – left > 0 then
2. mid ← (left + right ) / 2
3. mergeSort ( arr , n , barr , len , left , mid )
4. mergeSort ( arr , n , barr , len , mid + 1 , right )
5. int bLeft , bMid , bRight
6. bLeft ← barr [ left ]
7. bMid ← barr [ mid + 1 ] - 1
8. if right + 1 < len then
bRight ← barr [ right + 1 ] -1
9. else
bRight ← n - 1
10. merge ( arr , bLeft ,bMid , bRight )
算法merge (int arr[], int left , int mid , int right)
输入:待排序数组、数组的最左端下标、数组中间坐标、数组最右端下标
输出:临时排序数组
1. int lFirst ← left , rFirst ← mid + 1
2. int temp ← 0
3. int work [ right – left + 1 ]
4. while lFirst <= mid && rFirst <= right
5. do if arr [ lFirst ] < arr[ rFirst ]
6. work [ temp ++ ] ← arr[ lFirst ++ ]
7. else work[ temp ++ ] ← arr[ rFirst ++ ]
8. while lFirst <= mid
9. do work[ temp ++ ] ← arr[ lFirst ++ ]
10. while rFirst <=right
11. do work[ temp ++ ] ← arr[ rFirst ++ ]
12. for i ←left, j ← 0 ; i to right ; i++,j++
13. do arr [ i ] ← work [j]
实现代码
public class Main {
public static void main(String[] args) {
int[] arr = {4, 9, 2, 6, 1, 5, 7, 3};
int[] barr = new int[8]; //记录位置的数组
int len = fenge(arr, 8, barr); //分段段数
mergeSort(arr, 8, barr, len, 0, len - 1); //排序,len-1才是正确的下标
System.out.print(arr[0]);
for(int i = 1; i < 8; i++)
System.out.print(" " + arr[i]);
System.out.println();
}
public static int fenge(int[] arr, int n, int[] barr) {
int len = 1;
barr[0] = 0;
for(int i = 1; i < n; i++) {
if(arr[i] < arr[i - 1]) //如果后一位比前一位小,则len++,barr记录下小位数的下标+1
barr[len++] = i;
}
return len; //返回段数
}
public static void mergeSort(int[] arr, int n, int[] barr, int len, int left, int right) {
if(right - left > 0) { //如果大于零,证明还可以划分
int mid = (left + right) / 2;
mergeSort(arr, n, barr, len, left, mid); //前半段继续划分
mergeSort(arr, n, barr, len, mid + 1, right); //后半段继续划分
int bLeft, bMid, bRight; //用于与arr连接
bLeft = barr[left];
bMid = barr[mid + 1] - 1;
if(right + 1 < len) //判断右端的数又没有超出分割长度
bRight = barr[right + 1] - 1;
else
bRight = n - 1;
merge(arr, bLeft, bMid, bRight);
}
}
public static void merge(int[] arr, int left, int mid, int right) {
int lFirst = left, rFirst = mid + 1; //lFirst 为左边首位,rFirst为右边首位
int temp = 0;
int[] work = new int[right - left + 1]; //临时数值存放排序数组,长度为两个之和
while(lFirst <= mid && rFirst <= right) {
if(arr[lFirst] < arr[rFirst])
work[temp++] = arr[lFirst++];
else
work[temp++] = arr[rFirst++];
}
while(lFirst <= mid)
work[temp++] = arr[lFirst++];
while(rFirst <= right)
work[temp++] = arr[rFirst++];
for(int i = left, j = 0; i <= right; i++, j++) //复制临时数组到原数组中
arr[i] = work[j];
}
}
写得不好,多请谅解!