IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 神机百炼1.3-归并排序 -> 正文阅读

[数据结构与算法]神机百炼1.3-归并排序

归并排序导图

食用指南:

对该算法程序编写以及踩坑点很熟悉的同学可以直接跳转到代码模板查看完整代码
只有基础算法的题目会有关于该算法的原理,实现步骤,代码注意点,代码模板,代码误区的讲解
非基础算法的题目只有题目分析,代码实现,代码误区

题目描述:

  • 给定你一个长度为 n 的整数数列。

    请你使用归并排序对这个数列按照从小到大进行排序。

    并将排好序的数列按顺序输出。

    输入格式
    输入共两行,第一行包含整数 n。

    第二行包含 n 个整数(所有整数均在 1~109 范围内),表示整个数列。

    输出格式
    输出共一行,包含 n 个整数,表示排好序的数列。

    数据范围
    1≤n≤100000

  • 题目来源:https://www.acwing.com/problem/content/789/

题目分析:

  • 没有什么好分析的了,归并排序

算法原理:

基于分治

  • 将序列划分为左右两个区间,再将左右区间继续划分,直到不能继续划分为止;
  • 将相邻的两个小区间有序合并为一个大区间,直到合并回原本的序列
    归并排序示意图
  • 归并:两个小区间有序合并为一个大区间称为归并

和快速排序的区别

  • 快排:
    先做到安置好基准点位置和左右区间相对有序
    再递归进入左右区间
  • 归并:
    先递归进入左右区间
    再将左右区间有序合并为一个大区间
  • 总结:
    快排自上向下,归并自下向上
    快排由长到短,归并由短到长

时间复杂度:

  • O(n·logn),同于快速排序,都比C++内置的sort()速度略快
  • 递归状态树深度:
    每次将一段区间二分,共二分了logn次,状态树也是logn层
  • 状态树每层:
    归并时,每层的一个节点序列由遍历下一层的两个子序列合并而来;
    一层的所有节点代表的子序列之和就是原序列长度;
    每一层都是遍历上一层所有子序列合并而来,时间复杂度都是n

写作步骤:

三步走

  1. 粗暴二分作为分界点,mid = (l+r)>>1;
  2. 左右区间继续递归二分
  3. 将小的左右区间有序归并为大区间
  • 🌟最难的步骤在 第3步:有序归并
  • 🌟关键步骤也在 第3步:做到了左右区间有序合并

核心算法:

有序归并:

  • 双指针算法:
  1. i指针指向左序列arr[m]
    j指针指向右序列arr[n]
  2. 创建新序列,长度为m+n
  3. 比较arr[i] 和 arr[j]每次将较小的或等大的放入新序列中,放入新序列后该指针++,否则保持不变
  4. 当一个序列被指针遍历结束,而另一个还未结束,则将剩余部分全加入新序列中

代码注意:

  1. 先创建辅助合并数组tmp[10000]
  2. 由于递归处理左右区间,所以函数需要携带区间边界l,r参数
  3. 递归终止条件:l >=r
  4. 先递归到最底层,回溯时才合并左右子序列
  5. i,j指针分别负责合并左右区间,不可以越界,
    有一方全部并入后令一方剩余部分才全部并入
  6. 合并好的有序序列需要复制回原数组
  7. 二分的序列区间不可重复,一般这点写不错

代码模板:

#include <iostream>
using namespace std;
const int N = 100010;
int arr[N];
//加速点1:事先预备好归并辅助数组tmp
int tmp[N];
void merge_sort(int l, int r){
    if(l >= r) return;
    int mid = (l+r) >> 1;
    merge_sort(l,mid,arr);
    merge_sort(mid+1, r, arr);
    //注意点1:i从l到mid,j从mid+1到r
    int k = 0, i=l, j=mid+1;
    while(i<=mid && j<=r){
        if(arr[i] < arr[j]) tmp[k++] = arr[i++];
        else tmp[k++] = arr[j++];
    }
    while(i<=mid) tmp[k++ ]= arr[i++];
    while(j<=r) tmp[k++]  =arr[j++];
    //注意点2:归并结果需要从辅助数组tmp转移到原数组
    for(int i=l, j=0; i<=r; i++, j++){
        arr[i] = tmp[j];
    }
}
int main(){
    int n = 0;
    cin >> n;
    for(int i=0; i<n;i++){
        cin >> arr[i];
    }
    merge_sort(0,n-1);
    for(int i=0; i<n; i++){
        cout << arr[i]<<" ";
    }
}

代码误区:

1. 熟练度高后易错点:

  • 归并过程:
    两个子序列有序合并的时候,当有一方遍历完毕后,两方挑选的循环就结束
    进入另一方剩余部分整体并入有序序列的循环
    这是两个不同的循环,写快后容易遗忘第二个循环
  • 复原过程:
    将 [l, mid] 和 [mid+1, r]归并入辅助数组tmp后,需要从tmp数组复原到arr数组中

2. 答案错误原因分析:

  • 将 [l, mid] 和 [mid+1, r] 两个子串归并时,借助了三个计数变量-k=0,i=l,j=mid+1;
  • i负责遍历 [l, mid] 的子串,此处初始化i = l
  • j负责遍历 [mid+1, r] 的子串,此处初始化j = mid+1
  • k负责将有序结果记录入tmp[]
  • 最容易出错的是j的初始化,极可能初始化为mid,导致归并所得的序列重复了mid元素

本篇感想:

  • 本篇借助模板和之前的笔记完成速度明显加快,期待之后越写越快
  • 看完本篇博客,恭喜已登《练气境-初期》
    练气期初阶
    距离登仙境不远了,加油 登仙境初期
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-11 16:39:00  更:2022-05-11 16:40:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:25:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码