归并排序:
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,归并排序将两个已排序的表合并成一个表。
设两个有序的子序列(相当于输入序列)放在同一序列中相邻的位置上:array[low..m],array[m + 1..high],先将它们合并到一个局部的暂存序列 temp (相当于输出序列)中,待合并完成后将 temp 复制回 array[low..high]中,从而完成排序(可见归并排序并不是原址排序)。
在具体的合并过程中,设置 i,j 和 p 三个指针,其初值分别指向这三个记录区的起始位置。合并时依次比较 array[i] 和 array[j] 的关键字,取关键字较小(或较大)的记录复制到 temp[p] 中,然后将被复制记录的指针 i 或 j 加 1,以及指向复制位置的指针 p加 1。重复这一过程直至两个输入的子序列有一个已全部复制完毕(不妨称其为空),此时将另一非空的子序列中剩余记录依次复制到 array 中即可。
假设我们有一个没有排好序的序列,那么首先我们使用分割的办法将这个序列分割成一个个已经排好序的子序列。然后再利用归并的方法将一个个的子序列合并成排序好的序列。分割和归并的过程可以看下面的图例。
先"分割"再"合并"
从上图可以看出,我们首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据,再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列。
代码实现:
import java.util.Arrays;
import java.util.Comparator;
public class SortComparable {
@SuppressWarnings("rawtypes")
private static Comparator comparator;
// 按默认比较大小方法排序
public static <T> void sort(T[] array) {
if(!(array instanceof Comparable<?>[]))
throw new RuntimeException("该对象不可比较");
mergeSort(array, 0, array.length - 1);
}
// 按提供比较器方法比较大小排序
public static <T> void sort(T[] array, Comparator<T> comparator) {
SortComparable.comparator = comparator;
sort(array);
SortComparable.comparator = null;
}
// 代理默认比较方法或提供比较器比较大小的方法
@SuppressWarnings({ "unchecked", "rawtypes" })
private static <T> int compare(T o1, T o2) {
if(comparator == null)
return ((Comparable)o1).compareTo(o2);
else
return comparator.compare(o1, o2);
}
// 归并排序,分治法
private static <T> void mergeSort(T[] array, int begin, int end) {
if(begin == end)
return;
else {
int mid = (begin + end) / 2;
mergeSort(array, begin, mid);
mergeSort(array, mid + 1, end);
merge(array, begin, mid, end);
}
}
// 合并两个已排好序的部分,并复制回原数组上
@SuppressWarnings("unchecked")
private static <T> void merge(T[] array, int begin, int mid, int end) {
// 复制排好序的左右部分
T[] left = Arrays.copyOfRange(array, begin, mid + 1);
T[] right = Arrays.copyOfRange(array, mid + 1, end + 1);
int leftIndex = 0;
int rightIndex = 0;
// 比较左右最小的,复制回原数组,直到一边全部复制完
while(leftIndex < left.length && rightIndex < right.length) {
if(compare(left[leftIndex], right[rightIndex]) < 0) {
array[begin++] = (T)left[leftIndex++];
}
else
array[begin++] = (T)right[rightIndex++];
}
// 将剩余部分复制回原数组
Object[] still;
int stillIndex;
if(leftIndex < left.length) {
still = left;
stillIndex = leftIndex;
}
else {
still = right;
stillIndex = rightIndex;
}
while(begin <= end) {
array[begin++] = (T)still[stillIndex++];
}
}
}
***************************************************
一些辅助类:
public class Point implements Comparable<Point> {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point() {
this(0, 0);
}
// 比较大小
public int compareTo(Point other) {
if (x < other.x)
return -1;
else if (x > other.x)
return 1;
else {
if (y < other.y)
return -1;
else if (y == other.y)
return 0;
else
return 1;
}
}
// 计算两点间距离
public double distance(Point other) {
return Math.pow(
(x - other.x) * (x - other.x) + (y - other.y) * (y - other.y),
0.5);
}
public String toString() {
return "[" + x + ", " + y + "]";
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
*************************************************
import java.util.Comparator;
public class PointYComparaotr implements Comparator<Point> {
@Override public int compare(Point o1, Point o2) { if(o1.getY() < o2.getY()) return -1; else if(o1.getY() == o2.getY()) return 0; else return 1; }
}
********************************************
import java.util.Random;
// 自动生成随机点
public class PointGenerator {
private static Random rand = new Random(47);
private static int range = 100; // 默认范围100
@SuppressWarnings("static-access")
public PointGenerator(int range) {
this.range = range;
}
public static Point next() {
return new Point(rand.nextInt(range), rand.nextInt(range));
}
}
************************************************
测试类:
public class TestSortComparable {
public static void main(String[] args) {
Point[] pArray = new Point[50];
for(int i = 0; i < 50; i++)
pArray[i] = PointGenerator.next();
System.out.println("Point类默认排序:");
SortComparable.sort(pArray);
// 如果排好序的point数组存在前边比后边大的点,打印出“error”
for(int i = 0; i < pArray.length - 1; i++)
if(pArray[i].compareTo(pArray[i + 1]) > 0)
System.out.println("error");
System.out.println("Point类按提供的比较器排序:");
SortComparable.sort(pArray, new PointYComparaotr());
// 如果排好序的point数组存在前边比后边大的点,打印出“error”
for(int i = 0; i < pArray.length - 1; i++)
if(pArray[i].getY() > pArray[i+1].getY())
System.out.println("error");
}
}