归并排序其他博客已经介绍了很多,这里主要贴一下空间复杂度是O(5)的一个算法设计思路。

归并排序流程

归并排序是不断地将数组分成大小大致相同(偶数相同,奇数左小右大)的子数组,先对子数组进行排序,再进行合并的算法。归并的步骤不需要考虑,空间复杂度是出在合并字串上的,故下面对合并字串进行讨论。

合并字串

为方便叙述,对用到的符号和语言作如下规定:

  • 数组L,R表示左子串和右子串。R[i]语法与python相同。
  • len为数组长度,lenllen_llenl​为左子串长度,lenrlen_rlenr​为右子串长度。
  • 合并子串时,我们的左右字串已经是从小到大排列的了,这成为数组的结构
  • 称交换左右字串最右侧的元素,让整个数组最右侧的元素最小称为右交换;交换左右字串最左侧的元素,让整个数组最右侧的元素最大称为左交换。
    以从小到大排序举例。
    合并字串可以分为三种情况进行分析:
  1. L[-1] <= R[0]
    不用排序。
  2. L[0] >= R[-1]
    交换左右字串即可。具体方法(方法1)见下一节。
  3. 其他
    分别检查交换两侧的情况,选择不破坏结构的方向进行交换。结构不被破坏的充要条件是
    A:L[0]<=R[1]orL[n−2]<=R[n−1]A:L[0] <= R[1] or L[n - 2] <= R[n - 1]A:L[0]<=R[1]orL[n−2]<=R[n−1]
    即当A成立时进行左交换或右交换即可。当AAA不成立时,则有
    Aˉ:L[0]>R[1]andL[n−2]>R[n−1]\bar{A}:L[0] > R[1] and L[n-2] > R[n - 1]Aˉ:L[0]>R[1]andL[n−2]>R[n−1]
    成立。
    考察把左右子串交换位置1,得到:
    A′:R[0]>L[1]andR[n−2]>L[n−1]A':R[0] > L[1] and R[n-2] > L[n-1]A′:R[0]>L[1]andR[n−2]>L[n−1]
    此时发现AAA成立。进行右交换即可。

对于情况三,每一次进行交换都会缩短未整理的数组长度。因此,经过有限回合就能调整出有序数组。遗憾的是经过测试,按此方法合并子串的复杂度不是O(n),对于某些特殊的序列,需要O(2n)或O(3n)的复杂度才能得到有序数组。后面有时间会分析这种情况。

用到的方法

方法1

本节介绍一个空间复杂度为O(3)交换左右子串的方法。
注:本方法只能用于左右子串长度相等,或右串长度等于左串长度 + 1。因为归并算法保证左右子串长度相等,或右串长度等于左串长度 + 1,所以这个方法可以放心得在归并中使用。
定义 简单指针跳转数列:
p0=0pi+i={pi+lenr,pi<lenlpi−lenl,pi>=lenlp_0 = 0\\ p_{i+i} = \left\{\begin{matrix} p_i + len_r ,p_i < len_l \\ p_i - len_l ,p_i >= len_l \end{matrix}\right. p0​=0pi+i​={pi​+lenr​,pi​<lenl​pi​−lenl​,pi​>=lenl​​
该数列的使用方法是从i=0i = 0i=0开始,将索引为pip_ipi​的元素移动到pi+1p_{i+1}pi+1​处,到i=leni = leni=len为止。如对数组[4,5,1,2,3],lenl=2len_l = 2lenl​=2,p={0,3,1,2,4}p = {\{0,3,1,2,4\}}p={0,3,1,2,4},移动后为[1,2,3,4,5]。

方法22

这就是一个arr_move函数:

void Arr_move_m(int* arr, int len, int dir, int step);
移动数组元素位置。dir > 0 向右移动 step 步,dir < 0 向左移动 step 步,dir = 0 不移动。
消耗空间最少。

这里有一个实现,但是不怎么优雅,抽时间做一个更为优雅简洁的代码。

复杂度分析

经过测试,空间复杂度降低到常数的代价就是计算增加,不过也并没有飞起。
一般复杂度:1.5594929681717247
空间优化复杂度:2.0165047784704986
虽然这个算法的速度比常用的算法慢些,但也是我撸了几天代码撸出来的结果,在这里权当记录吧,也算是学习数据结构与算法课程的一点小思考。
关于算法性能度量,是用的我自己想的一个方法(没怎么学习过高性能分析,就全部自创了):

定义 基准速率比 为测试算法与快速排序算法运行 1 到 12 的全排列的
排序总时间之比

void check_speed(int n,bool show = false)
{cout << "运行中..." << endl;int a[n],b[n];clock_t reclaim = 0;for(int i = 1;i <= n;i++)a[i - 1] = i;clock_t myt=clock();do{for(int i = 0;i < n;i++)b[i] = a[i];}while(std::next_permutation(a,a+n));reclaim = clock() - myt;printf("初始化用时:%f s\t%ld\n",(long)reclaim / CLOCKS_PER_SEC,reclaim);myt=clock();do{for(int i = 0;i < n;i++)b[i] = a[i];qsort(b,n,sizeof(int),[](const void* a,const void* b)->int{return *(int*)a > *(int*)b;});}while(std::next_permutation(a,a+n));myt = clock() - myt - reclaim;printf("快速排序用时(n=%d): %ld\n", n, myt);for(int i = 1;i <= n;i++)a[i - 1] = i;myt=clock();do{for(int i = 0;i < n;i++)b[i] = a[i];if(show)show_a(a,n);Merge_sort(b,n,[](int a,int b)->bool{return a < b;});}while(std::next_permutation(a,a+n));myt = clock() - myt - reclaim;printf("测试排序用时(n=%d): %ld\n", n, myt);
}

ctime库时间处理的很粗糙,如果有人有更好的方法,欢迎留言交流。


  1. 对满足方法1的子串,采用方法1交换;不满足方法1的采用方法2交换。 ↩︎

  2. 最近看C++标准库,里面已经实现了这个方法:http://www.cplusplus.com/reference/algorithm/inplace_merge/ ↩︎

归并排序(常数空间复杂度的一个变体)相关推荐

  1. 子集和与一个整数相等算法_背包问题的一个变体:如何解决Java中的分区相等子集和问题...

    子集和与一个整数相等算法 by Fabian Terh 由Fabian Terh Previously, I wrote about solving the Knapsack Problem (KP) ...

  2. 变体类的使用 package record case【转载】

    **************理论区 start********************* DELPHI中记录的存储方式       在DELPHI中,我们用record关键字来表明一个记录,有时候,我 ...

  3. 如何写出正确的二分查找?——利用循环不变式理解二分查找及其变体的正确性以及构造方式...

    序言 本文以经典的二分查找为例,介绍如何使用循环不变式来理解算法并利用循环不变式在原始算法的基础上根据需要产生算法的变体.谨以本文献给在理解算法思路时没有头绪而又不甘心于死记硬背的人. 二分查找究竟有 ...

  4. C#泛谈 —— 变体(协变/逆变)

    有如下四个类. public class Animal{}public class Mammal : Animal{}public class Dog : Mammal{public void Eat ...

  5. VARIANT变体类型数据

    2019独角兽企业重金招聘Python工程师标准>>> 特殊 Variant 是一种特殊的数据类型,除了定长String数据及用户定义类型外,可以包含任何种类的数据.Variant ...

  6. 分布式存储图解_BERT的youxiu变体:ALBERT论文图解介绍

    作者:amitness 编译:ronghuaiyang 正文共: 3116 字 21 图 预计阅读时间: 9 分钟 原文链接: BERT的youxiu变体:ALBERT论文图解介绍​mp.weixin ...

  7. [Unity2018.3新功能]Prefab嵌套和变体

    本文节选自洪流学堂公众号专栏<郑洪智的Unity2018课>,未经允许不可转载. 洪流学堂公众号回复专栏,查看更多专栏文章. 大智:"今天我们要学的内容是Prefab中全新的部分 ...

  8. 变体类型Variant

    变体类型Variant,能够在运行期间动态的改变类型.变体类型能支持所有简单的数据类型,如整型.浮点.字符串.布尔型.日期时间.货币及OLE自动化对象等,不能够表达Object Pascal对象. 1 ...

  9. shader变体是什么_[Unity/shaderlab]关于着色器变体

    在Unity中可以通过#pragma multi_compile或者#pragma shader_feature指令来实现着色器多样化. 在运行时,相应的着色器变体是从材质的关键词中取得的(Mater ...

最新文章

  1. 补丁分发 linux,patch 命令应用补丁
  2. datatable删除一行数据
  3. php显示时间,php实现用已经过去多长时间的方式显示时间
  4. mysql like n_MySQL LIKE 子句
  5. 题解P3951【小凯的疑惑】
  6. php 文件锁 重发请求,PHP使用文件锁解决高并发问题示例
  7. 【学术研究】保持高效论文写作的10个原则!
  8. Android Studio Template(模板)开发,最后有参考
  9. JSP+JavaBean实现简单计算器
  10. java sqlite sqlite_busy_sqlite3出现SQLITE_BUSY错误码的原因以及解决方法
  11. ios python 越狱_如何使用Frida绕过iOS应用程序中的越狱检测!!!
  12. “软件工程”学习笔记、复习资料
  13. MFP450 基础飞行平台问世,支持室内室外无GPS环境飞行
  14. quill Cannot import ImageResize. Are you sure it was registered?
  15. 对深色(黑夜)模式的执着追求
  16. 程序员真实从零开始实操 赚钱渠道之二 群控+极速版
  17. 三维货架空间布局的优化算法——详细版
  18. 国内外表单工具软件参考
  19. 数字人民币试点新场景多点开花,多品种“硬件钱包”触达平常百姓家
  20. 拼多多大数据开发工程师SQL实战解析

热门文章

  1. Windows系列原版系统镜像下载
  2. linux没有无线wifi密码忘记,无线wifi密码忘记了怎么办_忘记无线网密码怎么办?-192路由网...
  3. c语言中按姓名查询成绩,求助 C语言学生系统中按照姓名进行查找学生的问题...
  4. 机器学习练习 2 - 逻辑回归
  5. 面试中的项目介绍怎么介绍?从哪些方面介绍?一文搞明白(大数据方向,其他方向可借鉴)
  6. 微信公众号开发详细教程
  7. 作为程序员,如何开展自己的副业?月赚三万的真实故事
  8. win10连接打印机共享显示0x0000011b错误代码怎么办?
  9. 【C语言每日一题】——猜凶手
  10. 结构体是纸老虎,旧知识新用法,easy