首页 > 编程语言 >必学排序算法——归并排序

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

时间:2024-10-21 21:48:09浏览次数:7  
标签:归并 int 必学 arr next ++ 数组 排序

目录

前言

归并排序算法是必须掌握的一种基础算法,在一些比较出名的竞赛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);
    }
};

九、结语

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

标签:归并,int,必学,arr,next,++,数组,排序
From: https://blog.csdn.net/ycs66/article/details/143085327

相关文章

  • 关于如何排序使得最终的答案最优的总结
    关于如何排序使得最终的答案最优的总结例题LuoguP1012CF2024C分析就以先CF2024C来展开,题意是给定\(N\)个二元组,确定一个可行的排列使得最后的序列逆序对个数最少,注意二元组内部不可以交换顺序Solution1详情见“CF980Review”中对这道题的解法,这里不多赘述了。只......
  • mongodb 查询条件,查询逻辑对照表,逻辑运算符,正则表达式匹配查询,排序,分页/巧分页,更新操
    mongodb查询条件,查询逻辑对照表,逻辑运算符,正则表达式匹配查询,排序,分页/巧分页,更新操作符,更新单个/多个文档,删除文档,批量插入,$type操作符,内嵌文档和数组查找修改1.条件查询SQLMQLa=1{a:1}a<>1{a:{$ne:1}}a>1{a:{$gt:1}}a>=1{a:{$gte:1}}a<1{a:{$lt......
  • 大模型驱动的电商个性化搜索结果重排序
    《大模型驱动的电商个性化搜索结果重排序》摘要随着电商行业的快速发展,个性化搜索成为了提升用户体验和提升转化率的关键因素。本文将探讨大模型驱动的电商个性化搜索结果重排序技术,分析大模型在电商搜索中的价值和应用原理。文章将详细介绍大模型的基础知识、电商搜索的......
  • 2023 ICPC Seoul Regional A. Apricot Seeds(Pjudge【NOIP Round #7】冒泡排序)
    题意一个序列,Q次询问一个区间[l,r],进行k轮冒泡后,求子区间[x,y]的和。(N<=1e6,Q<=5e5)冒泡定义为:fori=1ton-1:ifa[i]>a[i+1]:swap(a[i],a[i+1])考场想法:经典转01。110111000111000111111011100011100011111+1011100011100011111+1101100......
  • 基数排序1111
    相对于其他排序,基数排序不是将两个数作比较,通过将他们的个,十,百,千等位存入对应的桶中,桶可以为10个,对应着0-9,还有一点如果数字的最大位不同,那需将其他小于最大位的数前面补0,保持与最大位的数的位一致。进入的顺序和出去的顺序是一致的013021011052062043个0123456021,011052,06......
  • 八、手撕七大排序
    目录一、排序的概念及其应用1.1.概念1.2.应用一、常见的排序算法二、七大排序1.直接插入排序2.希尔排序3.选择排序4.堆排序5.冒泡排序6.快速排序6.1.hoare6.2.挖坑法6.3.前后指针6.4.优化三数取中7.归并排序一、排序的概念及其应用1.1.概念排序:所谓排序,......
  • sort排序
    sort是c++标准库中提供的排序算法,即快速排序。1.需要有序数据范围查询:如果题目要求对数据进行范围查询(如查找某个范围内的数),预先对数据进行排序可以提高查询效率。中位数问题:查找数组的中位数等问题,通常需要先将数组排序。2. 贪心算法任务调度:在贪心算法中,常常需要先将任......
  • 【洛谷 P1116】车厢重组 题解(模拟+冒泡排序)
    车厢重组题目描述在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车厢的顺序。于是他就负责用这座桥将进站的车厢按车厢号从小到大排列。他......
  • 【洛谷 P1116】车厢重组 题解(模拟+冒泡排序)
    车厢重组题目描述在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车厢的顺序。于是他就负责用这座桥将进站的车厢按车厢号从小到大排列。他......
  • 八大排序算法
    冒泡排序最简单的排序方法之一,且看其定义。定义:冒泡排序(BubbleSort)是一种简单的排序算法。它重复地遍历待排序的列表,比较每对相邻的项目,如果它们的顺序错误就把它们交换过来。遍历列表的工作是重复地进行直到没有再需要交换,也就是说该列表已经排序完成。这个算法的名字由来......