必学排序算法——归并排序

前言

归并排序算法是必须掌握的一种基础算法,在一些比较出名的竞赛acm、蓝桥杯,并且在一些公司面试题中都可能会出现,而且作为简单题我们必须要拿下,所以我们要引起重视,下面让我们来深入了解归并排序算法。
在这里插入图片描述

一、什么是归并排序

归并排序(Merge Sort)是一种经典的分治算法,它采用分治法将一个数组分成若干个子数组,分别对每个子数组进行排序,然后再将已排序的子数组合并成一个完整的排序数组。归并排序的时间复杂度为 O(nlogn),且它是一个稳定的排序算法。

二、归并排序步骤

1.分解:将数组从中间分成两半,分别对左半部分和右半部分进行归并排序。

2.递归:递归地对每一半进行分解,直到每个子数组只有一个元素(自然是有序的)。

3.合并:将两个有序的子数组合并成一个有序的数组。

三、算法思想

对于一个非有序的序列,可以拆成两个非有序的序列,然后分别调用归并排序,然后对两个有序的序列再执行合并的过程。所以这里的“归”其实是递归,“并”就是合并。​
  整个算法的执行过程用mergeSort(a[], l, r) 描述,代表 当前待排序数组a,左区间下标l,右区间下标r,分以下几步:​
  第一步、计算中点mid=(l+r)/2;​
  第二步、递归调用mergeSort(a[], l, mid)和mergeSort(a[], mid+1, r);​
  第三步、第二步中两个有序数组进行有序合并,再存储到 a[l:r];​
  调用时,调用mergeSort(a[], 0, n-1)就能得到整个数组的排序结果了。

四、归并排序复杂度分析

时间复杂度:
分解步骤的时间复杂度为 O(logn),因为数组被反复分成两半。
合并步骤的时间复杂度为 O(n),因为需要遍历两个子数组来合并它们。因此,总的时间复杂度为 O(nlogn)。

空间复杂度:
合并步骤需要一个额外的数组来存储合并后的结果,所以空间复杂度为 O(n)。

归并排序是一个相对简单且高效的排序算法,适用于大多数需要稳定排序的场景。

五、优化方案

归并排序在众多排序算法中效率较高,时间复杂度为0(nlog2n)。但是,由于归并排序在归并过程中需要额外的一个辅助数组,所以申请辅助数组内存空间带来的时间消耗会比较大,比较好的做法是,实现用一个和给定元素个数一样大的数组,作为函数传参传进去,所有的辅助数组干的事情,都可以在这个传参进去的数组上进行操作,这样就免去了内存的频繁申请和释放。

六、算法图解

在这里插入图片描述

七、c++代码模板

#include <iostream>  
#include <vector>  
  
// 合并两个有序的子数组  
void merge(std::vector<int>& arr, int left, int mid, int right) {  
    int n1 = mid - left + 1;  
    int n2 = right - mid;  
  
    // 创建临时数组  
    std::vector<int> L(n1), R(n2);  
  
    // 拷贝数据到临时数组L[]和R[]  
    for (int i = 0; i < n1; ++i)  
        L[i] = arr[left + i];  
    for (int j = 0; j < n2; ++j)  
        R[j] = arr[mid + 1 + j];  
  
    // 合并临时数组回到arr[left..right]  
    int i = 0, j = 0, k = left;  
    while (i < n1 && j < n2) {  
        if (L[i] <= R[j]) {  
            arr[k] = L[i];  
            ++i;  
        } else {  
            arr[k] = R[j];  
            ++j;  
        }  
        ++k;  
    }  
  
    // 拷贝L[]的剩余元素(如果有)  
    while (i < n1) {  
        arr[k] = L[i];  
        ++i;  
        ++k;  
    }  
  
    // 拷贝R[]的剩余元素(如果有)  
    while (j < n2) {  
        arr[k] = R[j];  
        ++j;  
        ++k;  
    }  
}  
  
// 归并排序的主函数  
void mergeSort(std::vector<int>& arr, int left, int right) {  
    if (left < right) {  
        int mid = left + (right - left) / 2;  
  
        // 递归排序两个子数组  
        mergeSort(arr, left, mid);  
        mergeSort(arr, mid + 1, right);  
  
        // 合并已排序的子数组  
        merge(arr, left, mid, right);  
    }  
}  
  
// 打印数组  
void printArray(const std::vector<int>& arr) {  
    for (int num : arr)  
        std::cout << num << " ";  
    std::cout << std::endl;  
}  
  
int main() {  
    std::vector<int> arr = {12, 11, 13, 5, 6, 7};  
    int arr_size = arr.size();  
  
    std::cout << "Given array is \n";  
    printArray(arr);  
  
    mergeSort(arr, 0, arr_size - 1);  
  
    std::cout << "\nSorted array is \n";  
    printArray(arr);  
    return 0;  
}

八、经典例题

1.排序数组

(帅哥们这个蓝色字体可以点进去看原题)

代码题解

class Solution {
    void merge(vector<int>&a,int l,int m,int r){
        int n1=m-l+1;//左部分元素个数
        int n2=r-m;//右部分元素个数
        int t[n1+n2];
        for(int i=0;i<n1;i++){
            t[i]=a[i+l];
        }
        for(int j=0;j<n2;j++){
            t[n1+j]=a[m+1+j];
        }
        int i=0,j=n1,k=l;
        while(i<n1&&j<n1+n2){
            if(t[i]<=t[j]){
                a[k++]=t[i++];
            }
            else a[k++]=t[j++];
        }
        while(i<n1){
            a[k++]=t[i++];
        }
        while(j<n1+n2){
            a[k++]=t[j++];
        }
    }
    void mergeSort(vector<int>&a,int l,int r){
        if(l>=r)return ;
        int m=(l+r)/2;//中点下标为m
        mergeSort(a,l,m);//将左半部分排好序
        mergeSort(a,m+1,r);//将右半部分排好序
        merge(a,l,m,r);//合并两个部分
    }
public:
    vector<int> sortArray(vector<int>& a) {
        mergeSort(a,0,a.size()-1);
        return a;
    }
};

2.排序链表

(帅哥们这个蓝色字体可以点进去看原题)

代码题解

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    ListNode* mergeSort(ListNode*a,ListNode*b){
        a=sortList(a);
        b=sortList(b);
        ListNode*head=new ListNode();
        ListNode*tail=head;
        while(a||b){
            if(!a){
                tail->next=b;
                break;
            }
            else if(!b){
                tail->next=a;
                break;
            }
            else if(a->val<b->val){
                tail->next=a;
                a=a->next;
            }
            else{
                tail->next=b;
                b=b->next;
            }
            tail=tail->next;
            tail->next=NULL;
        }
        return head->next;
    }
public:
    ListNode* sortList(ListNode* head) {
        if(!head)return NULL;
        else if(head->next==NULL)return head;
        ListNode*fast=head;
        ListNode*slow=head;
        ListNode*pre=NULL;
        while(fast){
            pre=slow;
            slow=slow->next;
            fast=fast->next;
            if(fast)fast=fast->next;
        }
        pre->next=NULL;
        return mergeSort(head,slow);
    }
};

九、结语

学习算法是一个很艰难,漫长的过程,我们要克服一切困难,学习算法本就是由易到难,我相信通过我们坚持不懈的努力一定能理解并熟练掌握其中细节,加油。
在这里插入图片描述
关注我让我们一起学习编程,希望大家能点赞关注支持一下,让我有持续更新的动力,谢谢大家
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值