归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。
分治算法的一般步骤:
(1)分解,将要解决的问题划分成若干规模较小的同类问题;
(2)求解,当子问题划分得足够小时,用较简单的方法解决;
(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解
首先考虑下如何将将二个有序数列合并。
合并两个有序的数组:
import java.util.Arrays; public class Main { static int[] arr={1,3,5}; static int[] arr2={2,4,6}; public static void main(String[] args) throws InterruptedException { int[] c= MemeryArray(arr,arr2); System.out.println(Arrays.toString(c)); } //将有序数组a[]和b[]合并到c[]中 static int[] MemeryArray(int[] a,int[] b) { int n=a.length; int m=b.length; int[] c=new int[m+n]; int i, j, k; i = j = k = 0; while (i < n && j < m) { if (a[i] < b[j]) c[k++] = a[i++]; else c[k++] = b[j++]; } while (i < n) c[k++] = a[i++]; while (j < m) c[k++] = b[j++]; return c; } }
[1, 2, 3, 4, 5, 6]
比较两个数组的第一个元素,选取其中较小的元素,假设数组为a,b,a[0]较小,选取a[0]赋值到c[0]
比较a[1]与b[0],选取其中较小的赋值到数组c
......
上面代码中最后的
while(i<n)/while(j<m) 是防止一个数组已经遍历合并完了,但是另外一个还没有合并完,再次进行合并。
============================================================
归并排序:
import java.util.Arrays; public class Main { static int[] array = {7, 8, 9, 6, 1, 4, 3, 2, 5, 0, -1, -2,-3}; public static void main(String[] args) throws InterruptedException { System.out.println(Arrays.toString(array)); mergeSort(array); System.out.println(Arrays.toString(array)); } //归并排序 public static void mergeSort(int[] array){ int[] workSpace = new int [array.length]; //用于辅助排序的数组 recursiveMergeSort(array,workSpace,0,workSpace.length-1); } /** * 递归的归并排序 * @param workSpace 辅助排序的数组 * @param lowerBound 欲归并数组段的最小下标 * @param upperBound 欲归并数组段的最大下标 */ private static void recursiveMergeSort(int[] array,int[] workSpace,int lowerBound,int upperBound){ if(lowerBound== upperBound){ //该段只有一个元素,不用排序 return; }else{ int mid = (lowerBound+upperBound)/2; recursiveMergeSort(array,workSpace,lowerBound,mid); //对低位段归并排序 recursiveMergeSort(array,workSpace,mid+1,upperBound); //对高位段归并排序 merge(array,workSpace,lowerBound,mid,upperBound); System.out.print("lowerBound:"+lowerBound+",mid:"+mid+",upperBound:"+upperBound+" "); display(array); } } /** * 对数组array中的两段进行合并,lowerBound~mid为低位段,mid+1~upperBound为高位段 * @param workSpace 辅助归并的数组,容纳归并后的元素 * @param lowerBound 合并段的起始下标 * @param mid 合并段的中点下标 * @param upperBound 合并段的结束下标 */ private static void merge(int[] array,int [] workSpace,int lowerBound,int mid,int upperBound){ int lowBegin = lowerBound; //低位段的起始下标 int lowEnd = mid; //低位段的结束下标 int highBegin = mid+1; //高位段的起始下标 int highEnd = upperBound; //高位段的结束下标 int j = 0; //workSpace的下标指针 int n = upperBound-lowerBound+1; //归并的元素总数 while(lowBegin<=lowEnd && highBegin<=highEnd){ if(array[lowBegin]<array[highBegin]){//将两者较小的那个放到workSpace中 workSpace[j++]= array[lowBegin++]; }else{ workSpace[j++]= array[highBegin++]; } } while(lowBegin<=lowEnd){ workSpace[j++]= array[lowBegin++]; } while(highBegin<=highEnd){ workSpace[j++]= array[highBegin++]; } for(j=0;j<n;j++){ //将归并好的元素复制到array中 array[lowerBound++]= workSpace[j]; } } //按顺序打印数组中的元素 public static void display(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } }
[7, 8, 9, 6, 1, 4, 3, 2, 5, 0, -1, -2, -3] lowerBound:0,mid:0,upperBound:1 7 8 9 6 1 4 3 2 5 0 -1 -2 -3 lowerBound:2,mid:2,upperBound:3 7 8 6 9 1 4 3 2 5 0 -1 -2 -3 lowerBound:0,mid:1,upperBound:3 6 7 8 9 1 4 3 2 5 0 -1 -2 -3 lowerBound:4,mid:4,upperBound:5 6 7 8 9 1 4 3 2 5 0 -1 -2 -3 lowerBound:4,mid:5,upperBound:6 6 7 8 9 1 3 4 2 5 0 -1 -2 -3 lowerBound:0,mid:3,upperBound:6 1 3 4 6 7 8 9 2 5 0 -1 -2 -3 lowerBound:7,mid:7,upperBound:8 1 3 4 6 7 8 9 2 5 0 -1 -2 -3 lowerBound:7,mid:8,upperBound:9 1 3 4 6 7 8 9 0 2 5 -1 -2 -3 lowerBound:10,mid:10,upperBound:11 1 3 4 6 7 8 9 0 2 5 -2 -1 -3 lowerBound:10,mid:11,upperBound:12 1 3 4 6 7 8 9 0 2 5 -3 -2 -1 lowerBound:7,mid:9,upperBound:12 1 3 4 6 7 8 9 -3 -2 -1 0 2 5 lowerBound:0,mid:6,upperBound:12 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]