归并排序是分治法中的一个实例,分治法是世界上最著名的通用算法设计技术之一。分治法的基本思想是:首先将带求解实例分解为相同规模的较小实例,然后对较小实例求解,最后将较小实例的解合并,得到整个实例的解。
所谓“归并”,就是将两个或两个以上的有序表合成一个有序表,这是归并排序中的核心操作。归并排序的基本思想分为三步(以数组排序为例):1、分,将待排序数组一分为二;2、排,对子数组采用同样的方法进行排序;3、和,将两个有序数组合并为一个有序数组。具体分析如下:
分:将待排序数组一分为二,若子数组的长度还大于1,则继续分。在这一步中,要将一个数组拆分成两个子数组,需要额外的存储空间。两个子数组的长度和等于待排序数组的长度,先要为两个子数组分配空间,然后将待排序数组的前一半复制给数组1,后一半复制给数组2。数组的复制容易,可以自己写一个数组复制的方法,也可以直接使用java中提供的数组复制的方法 System.arraycopy(Object src, int srcPos,Object dest, int destPos,int length); 其中的参数分别表示 起始数组、起始下标、目的数组、目的起始下标、复制长度。
排:对子数组使用同样的方法进行排序,大多数情况下使用递归,若子数组的长度大于1,继续分,直到长度等于1为止,进行下面的和并。
和:将有序的子数组合并为一个有序的数组,这是归并排序算法中的核心操作。完成这一步的思想是:设两个指针指向两个数组的起始下标,然后将两个元素进行比较,将较小的移入大数组中,并且这个指针往后移一位,重复这个步骤,直到其中一个子数组遍历完全,再将剩下的元素复制到大数组末尾。
伪代码如下:
void mergeSort(int[] a){
//使用归并排序算法对数组a进行排序
//输入:可排序数组a[0...n]
//输出:有序数组a[0...n]
if(a.length>1){ //当数组长度大于1时用归并排序进行排序
arrayCopy(a[0...n/2],b[0...n/2]); //将待排序数组一分为二
arrayCopy(a[n/2+1....n],c[0...n/2];
//对子数组用递归方式进行排序
mergeSort(b);
mergeSort(c);
merge(b,c,a); //将两个子数组再合并到原数组中
}
}
void merge(int[] a,int[] b,int[] c){
//将两个有序的子数组合并到数组c中
i←0;j←0;k←0;
while i<a.length && j<b.length {
if(a[i]<b[j]) {
c[k]←a[i];
k++;i++;
}else{
c[k]←b[j];
k++;j++;
}
}
//循环结束后要判断还有哪个数组由剩余
if i == a.length //表明数组a已经遍历完全
copy b[j...] to c[k....]
else copy a[i...] to c[k...]
}
package com.poe.sort;
import java.util.Scanner;
/**
* 归并排序:简单地说就是分与和,分:将待排序数组一分为二,再对其子数组用同样的方法排序 和:将相邻两个有序数组合并为一个有序数组 一般用递归的形式来表示
* 归并排序是稳定的,而快速排序和堆排序是不稳定的 归并排序缺点:需要额外的存储空间(要注意这个额外数组的分配位置,不能放在Merge中)
*
* @author Sam
*
*/
public class MergeSort {
private static int[] array; // 待排序数组,由命令行输入
private static int[] tempArray;// 额外的数组
public static void main(String[] args) {
Scanner sc = new Scanner(System.in); // 命令行输入类
array = new int[11]; // 为待排序数组分配空间
System.out.println("请输入元素");
for (int i = 0; i < array.length; i++) {
System.out.print("a[" + i + "]=");
array[i] = sc.nextInt();
System.out.println();
}
System.out.print("排序前数组为:");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
// 输完之后得到待排序数组array[0...9],调用归并排序方法进行排序
mergeSort(array);
System.out.print("排序后数组为:");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
//下面是使用System类中提供的数组复制的方法
// int[] t = new int[3];
// System.arraycopy(array, 3, t, 0, 3);
// for(int i = 0;i<3;i++){
// System.out.print("t["+i+"]="+t[i]+" ");
// }
}
/**
* 合并算法,将两个有序数组合并为一个有序数组
*
* @param a1有序数组一
* @param a2有序数组二
* @param a3合并成的新的数组
*/
public static void merge(int[] a1, int[] a2, int[] a3) {
int i = 0;// i指向数组a1
int j = 0;// j指向数组a2
int k = 0;// k指向数组a3
while (i < a1.length && j < a2.length) { // 两个子数组都还有元素
if (a1[i] <= a2[j]) { // 将小的元素移入合并的数组中,指针后移
a3[k] = a1[i];
k++;
i++;
} else {
a3[k] = a2[j];
k++;
j++;
}
}
// 循环结束后要判断还有哪个数组还有剩余,将剩余元素复制到新合并的数组中
if (i == a1.length) { // 表明数组a1已遍历完全,a2还有剩余
for (j = j; j < a2.length; j++) {
a3[k] = a2[j]; // 将a2中剩余的元素复制到a3末尾
k++;
}
} else {
for (i = i; i < a1.length; i++) {
a3[k] = a1[i];
k++;
}
}
// 到此,两个数组a1,a2已合并成一个有序数组a3
}
/**
* 用归并排序算法对数组进行排序
* @param a为待排序数组
*/
public static void mergeSort(int[] a) {
if (a.length > 1) {
int[] temp1 = new int[a.length / 2]; //创建一个临时数组,长度为a的一半
int[] temp2 = new int[a.length-temp1.length]; //创建两个临时数组,总长度为待排序数组的长度
//1、分:将待排序数组一分为二
aCopy(a, temp1, 0, a.length/2-1);
aCopy(a, temp2, a.length/2, a.length-1);
//2、对子数组递归调用同样方法进行排序
mergeSort(temp1);
mergeSort(temp2);
//3、和:将两个有序子数组合并为一个有序数组
merge(temp1, temp2, a);
}
}
// /**
// * 对相邻两个有序数组合并成一个有序数组
// * @param a1有序数组一
// * @param a2有序数组二
// * @param first
// * @param mid
// * @param end
// */
// public static void merge(int[] a1,int[] a2,int first,int mid,int end){
//
// }
//
// /**
// * 用归并排序算法对数组进行排序
// * @param a表示待排序数组
// * @param orign表示待排序数组的起始下标
// * @param end表示待排序数组的终止下标
// */
// public static void mergeSort(int[] a,int[] atemp,int orign,int end){
// if(orign