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

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');
	
 } 
`
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以回答这个问题。希尔排序、快速排序和归并排序都是常见的排序算法,它们在不同的场景下有着不同的优劣势。 首先,希尔排序是一种插入排序的改进版,它通过将待排序的序列按一定间隔分组,对每组使用插入排序,然后逐渐缩小间隔直至为1,最后对整个序列使用插入排序。希尔排序的时间复杂度为O(nlogn),但是由于它是一种不稳定的排序算法,所以在某些情况下可能会出现排序错误的情况。 其次,快速排序是一种分治思想的排序算法,它通过选择一个基准元素,将待排序的序列分成两个子序列,然后再对这两个子序列进行递归的排序。快速排序的时间复杂度为O(nlogn),但是由于它是一种不稳定的排序算法,而且在最坏情况下时间复杂度可能会达到O(n^2),所以需要对其进行优化。 最后,归并排序也是一种分治思想的排序算法,它将待排序的序列分成两个子序列,然后对这两个子序列进行递归的排序,最后将两个有序子序列合并成一个有序序列。归并排序的时间复杂度为O(nlogn),而且它是一种稳定的排序算法,但是它需要额外的空间来存储临时数组,所以在空间复杂度上可能会有一些问题。 综上所述,快速排序和归并排序都是基于分治思想的排序算法,它们的时间复杂度相对稳定,而希尔排序则是一种插入排序的改进版,它的时间复杂度也相对较稳定,但是在稳定性和最坏情况下的性能表现上可能不如快速排序和归并排序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值