算法思想的理解与分析
内容为针对归并排序的递归层次和元素频繁移动改进归并排序算法,在排序容量较小时直接使用链接表版本的插入排序,从而克服低层递归调用所带来的消耗过大的问题;容量较大时采用链接表版本的归并排序。传统归并排序算法使用了辅助数组B(1:n),算法在这附加空间上竭力地工作,在每次调用merge方法时,把存放在B(low:high)中的结果复制回A(low:high)去,我们考虑,使用一个以整数表示的链接信息数组link来替换暂时存放元素的辅助数组B,这样就可以节省一些附加空间。链接表中数值的意义为下一个元素的下标位置,因此链接表可以看作一个指针序列,链接表的归并不必移动元素本身,只要改变相应的链值即可,用0表示表的结束。
实现该算法使用了三个方法:insertSort方法、merge1方法和mergeSort1方法:
insertSort方法为链接表版本的插入排序,参数为要排列序列的起点和终点坐标,作用为更新link数组。首先定义一个head指针指向序列开头,在head的后一个起遍历整个序列,如果第i个位置的值小于head位置的值,就将link[i]的值置为head,即将head链接在当前i位置之后,再把head的值更新为当前位置i;如果第i个位置的值大于head位置的值,就从head开始在link数组中依次往后找,直到找到尚未链接的或者找到比当前i位置的值小的最大数为止,如果找到尚未链接的,就将当前位置i赋值给link数组中找到的尚未链接的位置,如果找到比当前i位置的值小的最大数,就将link数组的该值赋给link[i],意即将当前值链接在比当前值小的最大的数之后,再将i的值赋给link数组中找到的位置。
merge1方法的作用为使用链接表归并已排序的集合,参数为p、r和q,q和r是全程数组link(1:n)中两个表的指针,这两个表用来获取全程数组A(1:n)中已排序元素的子集合,该方法执行后构造出一个由p指示的新表,利用此表可以得到按非降序把A中元素排序好的元素表。首先找到p、r两个表开头中较小的作为新开头,之后对两个表的元素一一比较,选择较小的链接在开头后边,如果其中一个表结束了而另一个表还没结束,就将没结束的部分直接连接在后边。
mergeSort1方法利用辅助数组link(low:high)将全程数组A(low:high)按非降序排序。如果待排序序列的长度小于16,就直接调用insertSort方法进行插入排序,否则将待排序序列均分为两份,分别递归地调用mergeSort1方法进行排序。
package improvedMerge;
import java.util.Random;
import java.util.Scanner;
public class merge {
//作用:插入排序,更新link数组并返回link数组头指针
static int insertSort(int start, int end, int a[], int link[]){
int head= start;
for(int i=start+1;i<=end;i++){
if (a[i] <= a[head]) //如果当前位置的值小于head位置的值
{
link[i] = head;//将head连到当前位置的后面
head = i;//将当前位置作为新的head
}
else
{
int j = head;//从head开始找
while ((link[j] != -1) && (a[link[j]] <= a[i]))
j = link[j];//从head顺着link往后找,直到找到未连接的或者找到比当前值小 的最大值为止
if (link[j] == -1)//如果找到了未连接的
link[j] = i;
else //如果找到的是比当前值小的最大值
{//则将当前值插到这个值后面
link[i] = link[j];
link[j] = i;
}
}
}
return head;
}
static int merge1(int q, int r, int link[], int a[])
{
int head;
//找到两个开头中,最小的作为新开头
if (a[q] > a[r]) {
head = r;
r = link[r];
}
else {
head = q;
q = link[q];
}
int k = head;
int i = q, j = r;
while (i != -1 && j != -1) {
if (a[i] < a[j]) {
link[k] = i;
k = i;
i = link[i];
}
else {
link[k] = j;
k = j;
j = link[j];
}
}
if (i == -1)
link[k] = j;
else
link[k] = i;
return head;
}
static int mergeSort1(int low, int high, int a[], int link[])
{
if (high - low + 1 < 16)
{
int p = insertSort(low, high,a,link);
return p;
}
else
{
int mid = (low + high) / 2;
int q = mergeSort1(low, mid,link,a);
int r = mergeSort1(mid + 1, high,link,a);
return merge1(q, r,a,link);
}
}
public static void main(String[] args) {
int length = 0;
int begin = 0;
Scanner sc=new Scanner(System.in);
Random rd=new Random();
System.out.println("输入随机生成的数据个数:");;
length=sc.nextInt();
int a[] = new int[length];//待排序数组
int b[]= new int[length];//存放排好序的数组
int link[] = new int[length];//链接表
//用随机数填充待排序数组
System.out.println("排序前:");
for (int i = 0; i < length; ++i) {
a[i] = (rd.nextInt()) % 100;
System.out.print(a[i]+" ");//输出未排序的数组
link[i] = -1;//这里用-1初始化链接表,使得待排序数组下标可被全部利用
}
System.out.println();
//基于链接表的归并排序
begin = mergeSort1(0, length - 1,a,link);
//将原数组按link的次序放入数组b中,同时输出
int i = 0;
System.out.println("排序后:");
for (int j = begin; j != -1; j = link[j]) {
b[i++] = a[j];
System.out.print(a[j] + " ");
}
}
}