搜索
您的当前位置:首页归并排序以及三种常见优化

归并排序以及三种常见优化

来源:爱问旅游网

2020-8-16

原地归并代码

private void merge(Comparable[] a,int lo,int mid,int hi){
    for(int i=lo;i<=hi;i++){
        tep[i]=a[i];//辅助数组tep
    }
    int left=lo;//左部分开始
    int right=mid+1;//右部分开始元素下标
    for(int i=lo;i<=hi;i++){
        if(left>mid) a[i]=tep[right++];//左部分用完
        else if(right>hi) a[i]=tep[left++];//右部分用完
        else if(less(tep[left],tep[right])) a[i]=tep[right++];//比较大小
        else a[i]=tep[left++];//比较大小
    }
}

自顶向下:

package Algorithm;

public class Merge {
    private boolean less(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }
    Comparable[] tep;
    public void sort(Comparable[] a){
         tep=new Comparable[a.length];
         sort(a,0,a.length-1);
    }
    private void sort(Comparable[] a,int lo,int hi){
        if(hi<=lo) return;
        int mid=lo+(hi-lo)/2;
        sort(a,lo,mid);
        sort(a,mid+1,hi);
        merge(a,lo,mid,hi);
    }
    private void merge(Comparable[] a,int lo,int mid,int hi){
        for(int i=lo;i<=hi;i++){
            tep[i]=a[i];//辅助数组tep
        }
        int left=lo;//左部分开始
        int right=mid+1;//右部分开始元素下标
        for(int i=lo;i<=hi;i++){
            if(left>mid) a[i]=tep[right++];//左部分用完
            else if(right>hi) a[i]=tep[left++];//右部分用完
            else if(less(tep[left],tep[right])) a[i]=tep[right++];//比较大小
            else a[i]=tep[left++];//比较大小
        }
    }
}


自底向上:

可用于链表排序

  public void sort(Comparable[] a){
         tep=new Comparable[a.length];
         int n=a.length;
        for (int sz= 1; sz<n; sz=sz+sz) {//sz,数组大小(从0计算,o-5,则sz=5)
            for (int lo=0;lo<n-sz;lo+=sz+sz){//lo,数组开始下标。每次lo=lo+sz+sz,合并两个数组大小
                merge(a,lo,lo+sz-1,Math.min(n-1,lo+sz+sz-1));
            }
        }
    }

三种优化

1.小规模数组采用插入排序

private void sort(Comparable[] a,int lo,int hi){
        if(hi<=lo) return;
        
        if(hi-lo<=7){//小规模采用插入排序**********
        	for(int i=lo;i<=hi;i++){
				for(int j=i;j>lo;j--){
					if(a[j].compareTo(a[j-1]<0)){
						Comparable t=a[j];
						a[j]=a[j-1];
						a[j-1]=t;
					}
				}
			}
        }
        
        int mid=lo+(hi-lo)/2;
        sort(a,lo,mid);
        sort(a,mid+1,hi);
        merge(a,lo,mid,hi);
    }

2.合并时判断是否有序

private void sort(Comparable[] a,int lo,int hi){
        if(hi<=lo) return;
        int mid=lo+(hi-lo)/2;
        sort(a,lo,mid);
        sort(a,mid+1,hi);
        
        ****if(a[mid].compareTo(a[mid+1])<=0) return;*****//判断是否有序
        
        merge(a,lo,mid,hi);
    }

3.优化辅助数组时的时间

注意空间不能优化
将此辅助数组变为在递归中的参数即可。这样的话,上面两个代码都要改变。

package Algorithm;

public class Merge {
    private boolean less(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }
    private void exch(Comparable[] a,int i,int j){
        Comparable t=a[i];
        a[i]=a[j];
        a[j]=t;
    }
    public void sort(Comparable[] a){
        Comparable[] tep=a.clone();
        sort(tep,a,0,a.length-1);
    }
    private void sort(Comparable[] src, Comparable[] dst, int lo, int hi){
        if(hi<=lo) return;
        if(hi<=lo+7){
            for(int i=lo;i<=hi;i++){
                for(int j=i;j>lo;j--){
                  if(less(dst[j-1],dst[j])){
                      exch(dst,j,j-1);
                  }
                }
            }
            return;
        }
        int mid=lo+(hi-lo)/2;
        sort(dst,src,lo,mid);
        sort(dst,src,mid+1,hi);
        if(less(src[mid+1],src[mid])){
            System.arraycopy(src,lo,dst,lo,hi-lo+1);
            return;
        }
        merge(src,dst,lo,mid,hi);
    }

    private void merge(Comparable[] src,Comparable[] dst,int lo,int mid,int hi){
        int left=lo;//左部分开始
        int right=mid+1;//右部分开始元素下标
        for(int i=lo;i<=hi;i++){
            if(left>mid) dst[i]=src[right++];//左部分用完
            else if(right>hi) dst[i]=src[left++];//右部分用完
            else if(less(src[left],src[right])) dst[i]=src[right++];//比较大小
            else dst[i]=src[left++];//比较大小
        }
    }
}


参考自:https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/MergeX.java


2019-4-23
分解:将待排序的n个元素分成n/2两个子序列
解决:使用归并排序递归的排序两个子序列
合并:合并两个已排序好的子序列来生成排序好的答案。
当待排序的序列长度为1时,递归回升,因为这时长度为1的序列,已经排序好了。我们不需要任何操作。
这里讲一下具体操作:将要排序的一组数分成两堆(这里我们选取中间点为划分标准),然后我们从两堆中的第一个元素比较,小的(或者大的,这里要求我们是降序还是升序排列)就放到一个新开的数组里面,来保存我们所需要的答案。

#include<stdio.h>
void MERGE(int *num,int p,int q,int r)
{
	int n1,n2;//两边各多少个元素 
	n1=q-p+1;
	n2=r-q;
	int am[50]={0};
	int ab[50]={0};
	for(int i=0;i<n1;i++)
	{
		am[i]=num[p+i];
	}
	am[n1]=0x3f3f3f3f;//作为结束的哨兵
	for(int i=0;i<n2;i++)
	{
		ab[i]=num[q+1+i];
	}
	ab[n2]=0x3f3f3f3f;
	int j=0;
	int i=0;//分别是两个待排序数组的下标
	for(int k=p;k<=r;k++)//从p到r,保证了所有元素都被排序
	{
		if(am[i]<=ab[j])
		{
			num[k]=am[i];
			i++;
		}
		else
		{
			num[k]=ab[j];
			j++;
		}
	}
}
void MERGE_SORT(int *num,int p,int r)
{
	if(p<r)
	{
		int q=(p+r)/2;
		MERGE_SORT(num,p,q);
		MERGE_SORT(num,q+1,r);
		MERGE(num,p,q,r);
	}
}
int main(void)
{
	int num[50]={96,1,2,3,66,805,45,63,67,59,57,50,52,23,24};//15个
	printf("排序前\n");
	for(int j=0; j<=14; j++)
	{
		printf("%d ",num[j]);
	}
	putchar('\n');
	MERGE_SORT(num,0,14);
	printf("排序后\n");
	for(int j=0; j<=14; j++)
	{
		printf("%d ",num[j]);
	}
	putchar('\n');
	
 } 
`

因篇幅问题不能全部显示,请点此查看更多更全内容

Top