【归并排序】

本文详细介绍了归并排序的基本思想,包括其分治策略和通过分解与合并操作对数组进行排序的过程。通过C++代码实例演示了如何将数组进行归并排序,并探讨了时间复杂度O(nlogn)和空间复杂度O(n)。涉及的例题包括逆序对和链表排序等应用。
摘要由CSDN通过智能技术生成

一、归并排序的介绍

基本介绍

归并排序(MERGE- SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案修补”在一起,即分而治之)。

基本思想请添加图片描述
1.把数组从中间划分成两个子数组;
2.一直递归地把子数组划分成更小的子数组,直到子数组里面只有一个元素
3.依次按照递归的返回顺序,不断地合并排好序的子数组,直到最后把整个数组的顺序排好。

看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现的详细步骤:请添加图片描述

二、代码实现思想

按图所示实现分解方法
请添加图片描述

//int arr[]={8,4,5,7,1,3,6,2};
//int temp[]=new int[arr.length];
//分解方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
    if (left < right) {
        int mid = (left + right) / 2; //中间索引
        //向左递归进行分解
        //0 - mid 即是0 - 3 {8,4,5,7}
        mergeSort(arr, left, mid, temp);
        //向右递归进行分解
        //mid+1 - midright 即是4 - 7  {1,3,6,2}
        mergeSort(arr, mid + 1, right, temp);
    }
}

按图所示实现合并方法
请添加图片描述

//合并的方法
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
    //初始化i,左边有序序列的初始索引
    int i = left;
    //初始化j,右边有序序列的初始索引
    //为什么要mid+1?
    //因为假设数组arr{1,3,5,6,2,4} mid=(left+right)/2 = 2
    //此时左边i=left mid左边的就是 0 - mid 即是{1,3,5}
    //此时右边就是mid+1 - right 即是{6,2,4}
    int j = mid+1;
    
    int t = 0;//指向temp数组的当前索引
    //(一)
    //先把左右两边(有序)的数据按照规则填充到temp数组
    //直到左右两边的有序序列,有一边处理完毕为止
    //i <= mid 代表左边有序序列有值
    //j <= right 代表右边有序序列有值
    while (i <= mid && j <= right) {//继续
        //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
        //即将左边的当前元素,拷贝到temp数组
        //假设数组arr{1,3,5,6,2,4}
        //左边 0 - mid 即是{1,3,5}
        //右边 mid+1 -right 即是{6,2,4}
        //若arr[i]<= arr[j] 即是1 <= 6
        if (arr[i] <= arr[j]) {
            temp[t] = arr[i];//temp[0]=arr[i];
            t += 1;//指向temp数组下一位
            i += 1;//指向左边下一位arr[i+1]...
        }else{
            //反之arr[i] >= arr[j] 左边大于右边
            //则进行右边赋值给temp数组
            temp[t] = arr[j];//temp[0]=arr[i];
            t += 1;//指向temp数组下一位
            j += 1;//指向右边边下一位arr[j+1]...
        }
    }

    //(二)
    //把有剩余数据的一边的数据依次全部填充到temp
    //左边的有序序列还有剩余的元素,就全部填充到temp
    while( i <= mid){
        temp[t] = arr[i];
        t += 1;
        i += 1;
    }
    //右边的有序序列还有剩余的元素,就全部填充到temp
    while( j <= right){
        temp[t] = arr[j];
        t += 1;
        j += 1;
    }

    //(三)
    //将temp数组的元素拷贝到arr
    //为什么 t=0 ?
    //因为合并的时候按图所示数组:{8,4,5,7,1,3,6,2}
    //最先进入的是8,4 left=0 right = 1
    //经过上面的左边与右边比较,得出temp数组:4,8
    // 此时清空指向temp数组的下标指针t 重新回到0
    //tempLeft = 0 进行将temp数组里的4,8 赋值给arr数组
    t = 0;
    int tempLeft= left;
    while( tempLeft <= right){
        arr[tempLeft]=temp[t];
        t += 1;//赋值成功后指向temp数组的下标指针t往后移
        tempLeft +=1;//8,4 完成后到5,7 此时left=2 right = 3 ...
    }
}

分+合实现归并排序
请添加图片描述

//int arr[]={8,4,5,7,1,3,6,2};
//int temp[]=new int[arr.length];
//mergeSort(arr,0,arr.length-1,temp);
//System.out.println("并归排序后"+ Arrays.toString(arr));
//分解方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
    if (left < right) {
        int mid = (left + right) / 2; //中间索引
        //向左递归进行分解
        //0 - mid 即是0 - 3 {8,4,5,7}
        mergeSort(arr, left, mid, temp);
        //向右递归进行分解
        //mid+1 - midright 即是4 - 7  {1,3,6,2}
        mergeSort(arr, mid + 1, right, temp);
        //进行合并
        merge(arr,left,mid,right,temp);
    }
}

运行结果如下:
tempLeft:0     rigth:1
tempLeft:2     rigth:3
tempLeft:0     rigth:3
tempLeft:4     rigth:5
tempLeft:6     rigth:7
tempLeft:4     rigth:7
tempLeft:0     rigth:7
并归排序后[1, 2, 3, 4, 5, 6, 7, 8]

三、将数组归并排序C++代码:

#include <iostream>
#include<cstdio>
#include<vector>
#include<iterator>
#include<algorithm>
using namespace std;
//归并排序时间复杂度为O(nlogn)
void _sort(vector<int>&v,int begin,int end);
void merge_sort(vector<int>&v,int begin,int mid,int end);
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    while(cin >> n){
        vector<int>num(n);
        for(int i=0;i<n;++i)
            cin>>num.at(i);
            _sort(num,0,n-1);
            for(int i=0;i<num.size();++i)
                cout<<num[i]<<" ";
            cout<<endl;
            //copy(num.begin(),num.end(),ostream_iterator<int,char>(cout," "));
    }
    return 0;
}
void _sort(vector<int>&v,int begin,int end){
     if(begin<end){
         int mid=(begin+end)/2;
         _sort(v,begin,mid);
         _sort(v,mid+1,end);
         merge_sort(v,begin,mid,end);
     }
     return ;
}
 
void merge_sort(vector<int>&v,int begin,int mid,int end){	//从小到大排列
    vector<int>temp;
    int l=begin,m=mid+1,r=end;
    while(l<=mid&&m<=r){
        if(v.at(l)<=v.at(m)) {
        temp.push_back(v.at(l));
        ++l;
        }
        else {
        temp.push_back(v.at(m));
        ++m;
        }
    }
    while(l<=mid)
        temp.push_back(v.at(l++));
    while(m<=r)
        temp.push_back(v.at(m++));
    for(int i=0;i<temp.size();++i)
        v.at(i+begin)=temp.at(i);//注意这里一定要从begin开始
    return ;
}

四、复杂度分析

时间复杂度: T(n)
归并算法是一个不断递归的过程,假设数组的元素个数是n。
时间复杂度是T(n)的函数: T(n) = 2*T(n/2) + O(n)

怎么解这个公式呢?
对于规模为n的问题,一共要进行log(n)层的大小切分;
每一层的合并复杂度都是O(n);
所以整体的复杂度就是O(nlogn)

空间复杂度: O(n)
由于合并n个元素需要分配一个大小为n的额外数组,合并完成之后,这个数组的空间就会被释放

五、例题

1、BM20 数组中的逆序对.
2、单链表的排序
3、数组中的逆序对

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值