一.自顶向下的归并排序
package mergeSort;
import java.util.Arrays;
import java.util.Random;
/**
* 自顶向下的归并排序
*
*/
public class MergeSort {
private MergeSort(){
}
/**
* 对数组进行排序
* @param array 待排序数组
* @param n 数组长度
*/
public static void sort(Comparable[] array,int n){
//对数组array[0..n-1]进行排序
_mergeSort(array,0,n-1);
}
//对array[left..right]进行排序
private static void _mergeSort(Comparable[] array, int left, int right) {
//优化2可以在数组数量足够小的时候采用插入排序,在这里并没
if(left>=right)
return;
int mid=(left+right)/2;
_mergeSort(array, left, mid);
_mergeSort(array, mid+1, right);
//优化1,根据归并排序的性质,归并意思是将两边已经有序的数组合并成一个有序的数组
//所以只有当array[mid]>array[mid+1]才说明需要合并,否则这两个数组不需要操作合并到一起也是有序的
if(array[mid].compareTo(array[mid+1])>0)
{
merge(array,left,mid,right);
}
}
//对array[left..mid]和array[mid+1..right]进行合并操作
private static void merge(Comparable[] array, int left, int mid, int right) {
int l,r,k;
//申请辅助空间
Comparable[] helper=new Comparable[right-left+1];
for(int i=left;i<=right;i++)
{
helper[i-left]=array[i];
}
for(k=left,l=k,r=mid+1;l<r&&l<=mid&&r<=right;k++)
{
if(helper[l-left].compareTo(helper[r-left])<0)
{
array[k]=helper[l-left];
l++;
}
else{
array[k]=helper[r-left];
r++;
}
}
//如果array[left..mid]这边还有数据
while(l<=mid)
{
array[k++]=helper[l-left];
l++;
}
//array[mid+1..right]这边还有数据
while(r<=right)
{
array[k++]=helper[r-left];
r++;
}
}
//测试归并排序
public static void main(String[] args) {
//测试规模
int m=100000;
Random r=new Random();
Integer[]array=new Integer[m];
for(int i=0;i<m;i++)
{
array[i]=r.nextInt(100);
}
MergeSort.sort(array, array.length);
//排序后数组应当从小到大排序
for(int i=1;i<array.length;i++)
{
assert array[i-1]<=array[i];
}
}
}
二.自底向上的归并排序
package mergeSort;
import java.util.Arrays;
import java.util.Random;
/**
* 自底向上的归并排序
*/
public class MergeSortBU {
private MergeSortBU(){
}
public static void sort(Comparable[] array)
{
//控制归并数量级,size=x;意味着每次合并操作,将归并两个array长度为x的数组
for(int size=1;size<array.length;size+=size)
{
//控制以当前归并数量级,应当合并多少次
for(int j=0;j<array.length-size;j+=size+size)
{
merge(array,j,j+size-1,Math.min(j+size+size-1, array.length-1));
}
}
}
//对array[left..mid]和array[mid+1..right]进行合并操作
private static void merge(Comparable[] array, int left, int mid, int right) {
int l,r,k;
//申请辅助空间
Comparable[] helper=new Comparable[right-left+1];
for(int i=left;i<=right;i++)
{
helper[i-left]=array[i];
}
for(k=left,l=k,r=mid+1;l<r&&l<=mid&&r<=right;k++)
{
if(helper[l-left].compareTo(helper[r-left])<0)
{
array[k]=helper[l-left];
l++;
}
else{
array[k]=helper[r-left];
r++;
}
}
//如果array[left..mid]这边还有数据
while(l<=mid)
{
array[k++]=helper[l-left];
l++;
}
//array[mid+1..right]这边还有数据
while(r<=right)
{
array[k++]=helper[r-left];
r++;
}
}
//测试归并排序
public static void main(String[] args) {
//测试规模
int m=100000;
Random r=new Random();
Integer[]array=new Integer[m];
for(int i=0;i<m;i++)
{
array[i]=r.nextInt(100000);
}
MergeSortBU.sort(array);
//排序后数组应当从小到大排序
for(int i=1;i<array.length;i++)
{
assert array[i-1]<=array[i];
}
}
}
三.k路归并
对于选择几路归并取决于所给数组的数量,在本文中所给数组为3个,所以我采用的是三路归并.在本文中采用到了最小堆这种数据结构辅助算出每次归并操作的最小元素,读者也可以使用其他的方式求出最小元素即可,在本文中就不在叙述最小堆的代码了。思路:1.创建最小堆,并初始化堆的容量为k(如果是三路归并,k就等于3,)
2.将每一个数组中的第一个值加入到最小堆中,此时放入堆中的是我下面所创建的Data这种结构,可以方便后面取出该数组中后面的数据。
3.取出最小堆中的堆顶元素(在插入的过程中已经维护了最小堆的特性)加入到结果集中
4.得到刚才取出的堆顶元素所在数组的下一个元素,加入到最小堆中。
5.重复3,4过程直至完成所有的数组操作。
6.对于某个数组先于其他数组完成归并操作的处理:在本文中如果数组x先于其他数组全部加载进结果集中,则让最小堆的容量减少,再从最小堆中取其堆顶元素,重复3,4操作
package mergeSort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Merge_KWays {
//自定义数据 存放数据和它是第几个数组的
class Data implements Comparable<Data>{
private Comparable number; //元素的值
private int index; //该元素属于第index个数组
private int pos;//该元素在数组中的下标是pos
public Data(Comparable number,int index,int pos){
this.number=number;
this.index=index;
this.pos=pos;
}
public Comparable getNumber() {
return number;
}
public void setNumber(Comparable number) {
this.number = number;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public int getPos() {
return pos;
}
public void setPos(int pos) {
this.pos = pos;
}
@Override
public int compareTo(Data o) {
if(number.compareTo(o.getNumber())>0)
return 1;
else if(number.compareTo(o.getNumber())<0)
return -1;
else
return 0;
}
}
//最小堆 辅助归并算法
private MinHeap minHeap;
/**
* 开始进行归并算法
* @param listArray 要归并的数组的集合
* @param k k路归并
* @param result 归并后的结果
*/
//将所给数组集合进行合并,将合并的结果输出到result中
public void merge(List<Integer[]> listArray,int k,Integer[] result)
{
//创建最小堆辅助算法
minHeap=new MinHeap<>(k);
//将每个数组中的第一个存放到最小堆中
for(int i=0;i<k;i++)
{
minHeap.insert(new Data(listArray.get(i)[0],i,0));
}
for(int p=0;p<result.length;p++)
{
//取出最小的,并接着从最小的所在的数组中取出新元素放入到最小堆中
Data min = (Data)minHeap.extractMin();
result[p]=(Integer)min.getNumber();
int index = min.getIndex();
int pos = min.getPos();
//如果当前数组没有元素了,缩小堆的容量
if(listArray.get(index).length<=pos+1||listArray.get(index)[pos+1]==null)
{
continue;
}
minHeap.insert(new Data(listArray.get(index)[pos+1],index,pos+1));
}
}
public static void main(String[] args) {
Integer[] array={11,23,54,67,77};
Integer[] array1={2,32,42,44,65};
Integer[] array2={12,34,64,76,88,99};
List<Integer[]> listArray=new ArrayList<>(3);
listArray.add(array);
listArray.add(array1);
listArray.add(array2);
Merge_KWays merge_KWays=new Merge_KWays();
Integer[] result=new Integer[array.length+array1.length+array2.length];
merge_KWays.merge(listArray, 3, result);
System.out.println(Arrays.toString(result));
}
}
输出的结果:[2, 11, 12, 23, 32, 34, 42, 44, 54, 64, 65, 67, 76, 77, 88, 99]