排序算法(Java、C++)归并排序(二)

版权声明:版权声明:本文为博主原创文章,转载时请注明出处——作者:冰怜 https://blog.csdn.net/xiaoxin0630/article/details/88053576

 

归并排序

分成两边 左边就行排序 右边进行排序
然后左边在分成一半 右边也一样分成一半

不断往下分 直到成独立的就

要生成一个额外的空间,典型的空间换时间

往上归并的过程

左边和右边的进行比较谁小 就往新的空间插入


先下 然后又上 是不是觉得很像栈?这样就可以使用递归 进行解决这个问题

仔细看代码 递归 慢慢理解

        //递归使用归并排序,对arr[mid+1...r]两部分进行归并
	private static void sort(Comparable[] arr,int l,int r){
		
		if(l >=r)//基本问题,也是出口
			return ;
		
		int mid=(l+r)/2;
		sort(arr,l,mid);//左边的一半 一直递归排序
		sort(arr,mid+1,r);//右边的一半 一直递归排序
		merge(arr,l,mid,r);//往上归并
	}
	

然后是归并的代码

private static void merge(Comparable[] arr,int l,int mid,int r){
		
		Comparable[] aux=Arrays.copyOfRange(arr, l, r+1);//生成的额外的空间
		
		//初始化,i指向左边部分的起始位置l:j指向右半部分起始索引位置mid+1
		int i=l,j=mid+1;
		for(int k=l;k<=r;k++){
			
			if(i>mid){//如果左部分元素已经全部处理完毕
				arr[k]=aux[j-l];
				j++;
			}else if(j>r){//如果右半部分元素已经全部处理完毕
				arr[k]=aux[i-l];
				i++;
			}else if(aux[i-l].compareTo(aux[j-l]) <0){//左半部分所指元素 < 右半部分所指元素
				arr[k]=aux[i-l];
				i++;
			}else{//左半部分所指元素 >=右部分所指元素
				arr[k]=arr[j-l];
				j++;
			}
		}
	}

这里优化了一下

规模小于15 直接使用插入排序

// 递归使用归并排序,对arr[l...r]的范围进行排序
    private static void sort(Comparable[] arr, int l, int r) {

        // 优化2: 对于小规模数组, 使用插入排序
        if( r - l <= 15 ){
            InsertionSort.sort(arr, l, r);
            return;
        }

        int mid = (l+r)/2;
        sort(arr, l, mid);
        sort(arr, mid + 1, r);

        // 优化1: 对于arr[mid] <= arr[mid+1]的情况,不进行merge
        // 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
        if( arr[mid].compareTo(arr[mid+1]) > 0 )
            merge(arr, l, mid, r);
    }

Java整体的代码

package com.binglian.Sort;

import java.util.Arrays;

public class MergeSort {

	
	//我们的算法类不允许产生实例
	private MergeSort(){}
	
	private static void merge(Comparable[] arr,int l,int mid,int r){
		
		Comparable[] aux=Arrays.copyOfRange(arr, l, r+1);//生成的额外的空间
		
		//初始化,i指向左边部分的起始位置l:j指向右半部分起始索引位置mid+1
		int i=l,j=mid+1;
		for(int k=l;k<=r;k++){
			
			if(i>mid){//如果左部分元素已经全部处理完毕
				arr[k]=aux[j-l];
				j++;
			}else if(j>r){//如果右半部分元素已经全部处理完毕
				arr[k]=aux[i-l];
				i++;
			}else if(aux[i-l].compareTo(aux[j-l]) <0){//左半部分所指元素 < 右半部分所指元素
				arr[k]=aux[i-l];
				i++;
			}else{//左半部分所指元素 >=右部分所指元素
				arr[k]=arr[j-l];
				j++;
			}
		}
	}
	
	
	//递归使用归并排序,对arr[mid+1...r]两部分进行归并
	private static void sort(Comparable[] arr,int l,int r){
		
		if(l >=r)//基本问题,也是出口
			return ;
		
		int mid=(l+r)/2;
		sort(arr,l,mid);//左边的一半 一直递归排序
		sort(arr,mid+1,r);//右边的一半 一直递归排序
		merge(arr,l,mid,r);//往上归并
	}
	
	public static void sort(Comparable[] arr){
		
		int n=arr.length;
		sort(arr,0,n-1);
	}
	
	//测试MergeSort
	public static void main(String[] args) {
		
		//Merge Sort是我们学习的第一个O(nlogn)复杂度的算法
		//可以在1秒字后轻松处理100万数量级的数据
		//注意:不要轻易尝试使用SelectionSort,InsertionSort或者BubbleSort处理100万级的数据
		int N=1000000;
		Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
        SortTestHelper.testSort("com.binglian.Sort.MergeSort", arr);
	}

}

C++版本

//
// Created by liuyubobobo on 7/16/16.
//

#ifndef INC_04_MERGE_SORT_BOTTOM_UP_MERGESORT_H
#define INC_04_MERGE_SORT_BOTTOM_UP_MERGESORT_H

#include <iostream>
#include <algorithm>
#include "InsertionSort.h"

using namespace std;


// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
template<typename  T>
void __merge(T arr[], int l, int mid, int r){

    //* VS不支持动态长度数组, 即不能使用 T aux[r-l+1]的方式申请aux的空间
    //* 使用VS的同学, 请使用new的方式申请aux空间
    //* 使用new申请空间, 不要忘了在__merge函数的最后, delete掉申请的空间:)
    T aux[r-l+1];
    //T *aux = new T[r-l+1];

    for( int i = l ; i <= r; i ++ )
        aux[i-l] = arr[i];

    // 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
    int i = l, j = mid+1;
    for( int k = l ; k <= r; k ++ ){

        if( i > mid ){  // 如果左半部分元素已经全部处理完毕
            arr[k] = aux[j-l]; j ++;
        }
        else if( j > r ){  // 如果右半部分元素已经全部处理完毕
            arr[k] = aux[i-l]; i ++;
        }
        else if( aux[i-l] < aux[j-l] ) {  // 左半部分所指元素 < 右半部分所指元素
            arr[k] = aux[i-l]; i ++;
        }
        else{  // 左半部分所指元素 >= 右半部分所指元素
            arr[k] = aux[j-l]; j ++;
        }
    }

    //delete[] aux;
}

// 使用优化的归并排序算法, 对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort(T arr[], int l, int r){

    // 对于小规模数组, 使用插入排序
    if( r - l <= 15 ){
        insertionSort(arr, l, r);
        return;
    }

    int mid = (l+r)/2;
    __mergeSort(arr, l, mid);
    __mergeSort(arr, mid+1, r);

    // 对于arr[mid] <= arr[mid+1]的情况,不进行merge
    // 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
    if( arr[mid] > arr[mid+1] )
        __merge(arr, l, mid, r);
}

template<typename T>
void mergeSort(T arr[], int n){

    __mergeSort( arr , 0 , n-1 );
}

#endif //INC_04_MERGE_SORT_BOTTOM_UP_MERGESORT_H

还有一种直接往上,因为省略了往下排序,所以不用递归都可以实现,用迭代

for( int sz = 16; sz < n ; sz += sz )

sz+=sz 就是当前加上自己 比如看一个元素,然后加上自己 第二个 然后再是第四个 就是往上的归并

public static void sort(Comparable[] arr){

        int n = arr.length;

        // Merge Sort Bottom Up 无优化版本
//        for (int sz = 1; sz < n; sz *= 2)
//            for (int i = 0; i < n - sz; i += sz+sz)
//                // 对 arr[i...i+sz-1] 和 arr[i+sz...i+2*sz-1] 进行归并
//                merge(arr, i, i+sz-1, Math.min(i+sz+sz-1,n-1));

        // Merge Sort Bottom Up 优化
        // 对于小数组, 使用插入排序优化
        for( int i = 0 ; i < n ; i += 16 )
            InsertionSort.sort(arr, i, Math.min(i+15, n-1) );

        for( int sz = 16; sz < n ; sz += sz )
            for( int i = 0 ; i < n - sz ; i += sz+sz )
                // 对于arr[mid] <= arr[mid+1]的情况,不进行merge
                if( arr[i+sz-1].compareTo(arr[i+sz]) > 0 )
                    merge(arr, i, i+sz-1, Math.min(i+sz+sz-1,n-1) );

    }

猜你喜欢

转载自blog.csdn.net/xiaoxin0630/article/details/88053576