算法学习笔记(5.0)-基于比较的高效排序算法-归并排序

##时间复杂度O(nlogn)

目录

##时间复杂度O(nlogn)

##递归实现归并排序

##原理

##图例

##代码实现

##非递归实现归并排序

##释

#代码实现


##递归实现归并排序

##原理

是一种基于分治策略的基础排序算法。

1.划分阶段:通过不断递归地将数组从中点处分开,将长数组的排序问题转化成段数组的排序问题。

2.合并阶段:当子数组的长度为1时终止划分,开始合并,持续不断地将左右两个较短的有序数组合并为一个较长的有序数组,直到结束。

##图例

##代码实现

1.向下递归,对半分割

2.递归返回条件:递归到最小,1个就是有序【控制的是范围,归并的是区间】

3.递归到最深处,最小时,就递归回去,开始分割按对半分割出的范围,将已有子序列合并,在tmp里面进行合并

4.再将tmp里形成的有序序列,拷贝回原数组【因下一次递归回去上一层的归并过程中,会将数据在tmp中进行归并,会将tmp中的数据覆盖,因此要及时,拷】

//python代码示例
def Merge(a, start, mid, end) :
    tmp = [] #创建一个临时列表,用来存储合并的值
    #两段起点的开始
    l = start
    r = mid + 1
    #当两段都没到达末尾,则继续循环,将其添加到tmp
    while l <= mid and r <= end :
        if a[l] <= a[r] :
            tmp.append(a[l])
            l += 1
        else :
            tmp.append(a[r])
            r += 1
    #此时肯定只剩下,单一的一个值,即将剩余元素塞入到tmp中
    tmp.extend(a[l:mid+1])
    tmp.extend(a[r:end+1])
    #将合并完成的元素,赋值到a原数组中
    for i in range(start,end+1):
        a[i] = tmp[i - start]
    print(start,end,tmp)

def MergeSort(a,start,end) :
    #利用递归来实现归并排序,将大的问题分解成小的子问题
    if start == end : #当递归到数组只有一个元素时,直接返回
        return
    mid = (start + end) // 2 #计算出mid,找到递归点
    MergeSort(a,start,mid) #左递归
    MergeSort(a,mid+1,end) #右递归

    Merge(a,start,mid,end)
    #左递归,左合并,右递归,右合并,类似于树的后序遍历

a = [7,3,2,6]
MergeSort(a,0,3)

//c++代码实现示例
#include<iostream>
#include<vector>
using namespace std;
//c++代码实现示例
void merge(vector<int> &a, int left, int mid, int right)
{
    int i = left ;
    int j = mid + 1 ;
    int k = left ;
    vector<int> tmp(right - mid + 1) ;
    while ( i <= mid && j <= right )
    {
        if ( a[i] <= a[j] )
        {
            tmp[k++] = a[i++] ;
        }
        else
        {
            tmp[k++] = a[j++] ;
        }
    }
    while ( i <= mid )
    {
        tmp[k++] = a[i++] ;
    }
    while ( j <= right )
    {
        tmp[k++] = a[j++] ;
    }
    for (int m = left ; m <= right ; m++)
    {
        a[m] = tmp[m - left] ;
    }
    cout << left << right << " " ;
    for (int n = 0 ; n < tmp.size() ; ++n)
    {
    	
    	cout << tmp[n] << " " ;
	}
}


void mergeSort(vector<int> &a, int left, int right)
{
    if (left >= right)
    {
        return ;
    }
    int mid = (left + right) / 2 ;
    mergeSort(a,left,mid) ;
    mergeSort(a,mid+1,right) ;
    merge(a,left,mid,right) ;
}

int main()
{
//	vector<int> arr = {1,2,3,4,5};
//	
	vector<int> arr;
    arr.push_back(7);
    arr.push_back(3);
    arr.push_back(2);
    arr.push_back(6);
//    arr.push_back(5);
	mergeSort(arr,0,3) ;
	
	return 0 ;

}
void _MergerSort(DataType* a ,DataType* tmp, int begin,int end)
{
	//递归返回条件,不正常的范围,或只剩下一个数 
	if (begin >= end)
	{
		return ;
	}
	int mid = (begin + end) / 2 ;
	//递归过程 
	_MergeSort(a,tmp,begin,mid) ;
	_MergeSort(a,tmp,mid+1,end) ;
	
	//合并过程 
	//归并到tmp数组中,再拷贝回去 
	int begin1 = begin ;
	int end1 = mid ;
	int begin2 = mid + 1 ;
	int end2 = end ;
	//指向tmp,=begin是 根据要进行比较插入的数组的位置 找到其对应在tmp中所对应的位置,则不会覆盖前面已经排好序的数据
	int index = begin ;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin++] ;
		}
		else
		{
			tmp[index++] = a[begin2++] ;
		}
	}
	//剩下还没有插入进去的 
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++] ;
	}
	
	//拷贝回去原数组a中
	memcpy(a+begin,tmp+begin,sizeof(DataType)*(end-begin+1)) ; 
}

void MergeSort(DataType* a, int n)
{
	DataType* tmp = (DataType*)malloc(sizeof(DataType)*n) ;
	if (tmp == NULL)
	{
		perror("malloc fail") ;
		return ;
	}
	_MergeSort(a,tmp,0,n-1) ;
	free(tmp) ;
}

对于链表,归并排序相较于其他排序算法具有显著优势,可以将链表排序任务的空间复杂度优化至 𝑂(1) 。

  • 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。
  • 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链表合并为一个长有序链表)无须创建额外链表。

##非递归实现归并排序

##释

归并排序时二分的思想=>logN层=>递归不会太深、且现在编译器优化后,递归、非递归的性能差距没有特别大了=>所以可以不用考虑非递归。

递归的缺点:递归消耗栈帧,递归的层数太深,容易爆栈

【栈的空间比较小,在x86(32位)环境下,只有8M。(对比同一环境下的堆,则有2G+)。因为平时函数调用开不了多少个栈帧。理论上递归深度>1w 可能就会爆 ,但实际上5k左右就爆掉了】,这时就需要改非递归了。

非递归改写方法:

1.改变循环

2.利用数据结构栈(本质是通过malloc在堆上开辟的存储空间,内存空间需要足够大)

3.递归逆着求-实际上也差不多也是递归思想(如斐波那契数列逆着来求也是可行的)

#代码实现

  1. 开辟新的数组(临时存放)用于归并排序过程
  2. int gap=1;gap*=2【gap控制归并的范围:11归并,22归并,44归并】
  3. for (int i = 0; i < n; i += 2 * gap) { 【i 控制进行比较轮到的组号,控制进行归并的组号】
  4. 归并完一轮,将归并好的有序数组拷贝回原数组memcpy 。
  5. 进入新的一轮归并,直至gap>n则归并完成

     ☆注意的两个情况

     6. if (begin2 >= n) { break; } 第二组不存在,这一组不用归并了 

     7. if (end2 > n) { end2 = n - 1; } 第二组右边界越界问题,修正一下

 
void MergerSortNonR(DataType* a, int n)
{
    DataType* tmp = (DataType*)malloc(sizeof(DataType) * n);   //开辟新的数组(临时存放)用于归并排序过程
    
    if (tmp == NULL)
    {
        perror("malloc fail") ;
        return ;
    }
    
    int gap = 1 ;
    
    while (gap < n) 
    {
        for (int i = 0 ; i < n ; i += 2 * gap)
        {
//            // 1 , 1 归并 
//            // 2 , 2 归并
//            //4 , 4 归并 
//            //[begin1,end1][begin2,end2]归并 
            int begin1 = i ;
            int end1 = i + gap - 1 ;
            
            int begin2 = i + gap ;
            int end2 = i + 2 * gap - 1 ;

//            //如果第二组不存在,这一组不用归并了
              if (begin2 >= n) 
            {
                   break;
              }
              //第二组右边界越界问题,修正一下
              if (end2 > n) 
            {
            end2 = n - 1;
              }
//            
//            //当beigin2 超过 n 的时候,直接break掉就OK了
//            //但end2 超过 n 的时候,需要修改边界问题 n - 1 
//            
            int index = i ;
            
            while (begin1 <= end1 && begin2 <= end2)
            {
                if (a[begin1] < a[begin2])
                {
                    tmp[index++] = a[begin1++] ;
                }
                else
                {
                    tmp[index++] = a[begin2++] ;
                }
            }
            
            while (begin1 <= end1)
            {
                tmp[index++] = a[begin1++] ;
            }
            
            while (begin2 <= end2)
            {
                tmp[index++] = a[begin2++];
            }
//            //为什么这里不能是,2 * gap呢,不一定是2*gap的数都拷贝过去,memcpy(a+i,tmp+i,sizeof(DataType)*(end2 - beigin1 + 1)) 错误 
//            // memcpy(a+i,tmp+i,sizeof(DataType)*(end2 - begin1 + 1)) ; 因为begin1++会发生改变的,因此不可以,错误 
            memcpy(a+i,tmp+i,sizeof(DataType)*(end2 - i + 1)) ;
//            
//        }
        printf("\n");
//        for (int k = 0 ; tmp.size() ; k ++)
//        {
//            printf("%d",tmp[k]) ;
//        }
        gap = gap * 2 ;
    }
    free(tmp);
}
}
 
def MergeSort(a, n) :

    tmp = [0] * (n+1)

    gap = 1

    while (gap < n) :
        z = gap * 2
        for i in range(0,n,z) :

            begin1 = i
            end1 = i + gap - 1

            begin2 = i + gap
            end2 = i + 2 * gap - 1

            if begin2 > n :
                break
            if end2 > n :
                end2 = n - 1

            index = i

            while begin1 <= end1 and begin2 <= end2 :
                if a[begin1] < a[begin2] :
                    tmp[index] = a[begin1]
                    index += 1
                    begin1 += 1
                else :
                    tmp[index] = a[begin2]
                    index += 1
                    begin2 += 1

            while begin1 <= end1 :
                tmp[index] = a[begin1]
                index += 1
                begin1 += 1
            while begin2 <= end2 :
                tmp[index] = a[begin2]
                index += 1
                begin2 += 1

            for j in range(i,end2 + 1) :
                a[j] = tmp[j - i]

        print()
        # print(tmp)
        gap = gap * 2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/630324.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

迷宫游戏(c++)

我们来玩一个迷宫游戏&#xff0c;尝试走一下面的迷宫。 迷宫游戏 我们用一个二维的字符数组来表示前面画出的迷宫&#xff1a; S**. .... ***T 其中字符S表示起点&#xff0c;字符T表示终点&#xff0c;字符*表示墙壁&#xff0c;字符.表示平地。你需要从S出发走到T&#xf…

【全开源】JAVA共享自习室共享学习室无人系统支持微信小程序+微信公众号+H5

开启智能学习新时代 随着社会的快速发展&#xff0c;人们对于学习环境的需求也日益增加。为满足这一需求&#xff0c;我们推出了“共享自习室系统源码”&#xff0c;旨在通过智能化的管理方式&#xff0c;打造高效、便捷、舒适的共享学习空间。 核心功能 自习室预约&#xf…

6. 网络编程-网络io与select、poll,epoll

https://0voice.com/uiwebsite/html/courses/v13.7.html 首先看看这个学习计划 网络、网络编程、网络原理基础组件&#xff0c;20个。中间件 Redis ,MySQL&#xff0c;Kafka&#xff0c;RPC&#xff0c;Nginx开源框架&#xff08;解决方案&#xff09;业务开发(工程师开发&am…

YOLOv9训练自己的数据集:最新最详细教程

一、代码及论文链接&#xff1a; 代码链接&#xff1a;https://github.com/WongKinYiu/yolov9/tree/main 论文链接&#xff1a;https://arxiv.org/abs/2402.13616 二、使用步骤 1.1 虚拟环境配置 创建一个虚拟环境用于单独对yolov9的环境进行配置&#xff1a; conda crea…

Java中的数组、Set、List、Map类型的互相转换总结

序言 数组、Set、List、Map是Java语言非常常用的几种数据类型&#xff0c;他们之间存在着千丝万缕的联系。关于底层的数据结构我这里就不再多说啦&#xff0c;直接从应用出发&#xff0c;总结他们之间的转换方法&#xff0c;并给出推荐方法。 大家可以点赞收藏等到需要的时候…

传说中的运维门户设计

在IT服务管理这片广阔天地中&#xff0c;运维门户如同一位技艺高超的魔术师&#xff0c;轻轻一挥手&#xff0c;便将纷繁复杂的运维世界化繁为简&#xff0c;编织成一张便捷高效、触手可及的网络。它不仅是ITSM系统中不可或缺的一环&#xff0c;更是连接用户与技术世界的桥梁&a…

【打字】打字训练之针对性键盘区域练习

本文章的核心点是&#xff1a;使用代码生成自己想要训练的键位的词汇&#xff0c;然后导入到打字软件针对性练习 一个程序员突然想纠正打字习惯源于腱鞘炎&#xff0c;虽然使用双拼打字已经不慢了&#xff0c;但是姿势不是很正确&#xff0c;导致了腱鞘炎。 所以想着好好纠正指…

就这?轻轻松松在RK356X Android11适配ML307R Cat.1模组

开源鸿蒙硬件方案领跑者 触觉智能 Industio 本文基于IDO-SXB3568主板&#xff0c;介绍Android11平台上适配中移物联ML307R Cat.1 4G模组的方法。该方法适用于触觉所有RK356X的主板。 IDO-SXB3568是触觉智能推出的RK3568行业主板&#xff0c;预计6月上旬正式上架售卖。该行业主…

Docker安装Mosquitto

在物联网项目中&#xff0c;我们经常用到MQTT协议&#xff0c;用MQTT协议做交互就需要部署一个MQTT服务&#xff0c;而mosquitto是一个常用的MQTT应用服务&#xff0c; Mosquitto是一个实现了消息推送协议MQTT v3.1的开源消息代理软件。MQTT&#xff08;Message Queuing Teleme…

【淘宝超高价女装】电商最好项目:一单赚1000多

课程目录 01.【超高价女装】项目介绍实操案例 02.【超高价女装】找款&#xff1a;配得上1000多的款式 03.【超高价女装】软件上款&#xff1a;600个款为底 04.【超高价女装】标题&#xff1a;能卖1000多的标题 05.【超高价女装】销量布局&#xff1a;主推款做销量评价 06…

【python量化交易】—— Alpha选股策略 - Qteasy自定义交易策略【附源码】

使用qteasy创建并回测Alpha选股交易策略 使用qteasy创建并回测Alpha选股交易策略策略思想第一种自定义策略设置方法&#xff0c;使用持仓数据和选股数据直接生成比例交易信号PS信号&#xff1a;第二种自定义策略设置方法&#xff0c;使用PT交易信号设置持仓目标&#xff1a;第三…

代码审计--变量覆盖

漏洞原理 变量覆盖(Dynamic Variable Evaluation) 是指变量未被初始化&#xff0c; 而我们自定义的变量可以替换程序原有的变量值。 相关函数 $$ &#xff0c; extract &#xff0c; parse_str &#xff0c; import_request_variables 等等 这里涉及到一个安全函数&#xf…

嬴政只比刘邦大三岁,项羽竟是比刘邦小24岁的小老弟?

大秦始皇帝祖龙嬴政、汉太祖高皇帝刘邦、西楚霸王项羽他们的出生顺序怎样&#xff1f; 细看正史我们就知道&#xff0c;项羽嬴政刘邦这三个人&#xff0c;说他们是兄弟可以&#xff0c;说他们有代差也不错&#xff1a;公元前259年&#xff0c;秦始皇降生&#xff0c;三年后的…

Blender 导入资源包的例子

先到清华源下载资源包&#xff1a; Index of /blender/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 具体地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/blender/demo/asset-bundles/human-base-meshes/human-base-meshes-bundle-v1.1.0.zip 解压/hum…

【数据结构】C++语言实现二叉树的介绍及堆的实现(详细解读)

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

【Leetcode每日一题】 动态规划 - 简单多状态 dp 问题 - 删除并获得点数(难度⭐⭐)(76)

1. 题目解析 题目链接&#xff1a;LCR 091. 粉刷房子 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 1. 状态定义 在解决这类问题时&#xff0c;我们首先需要根据题目的具体要求来定义状态。针对房屋粉刷问题&#…

malloc_consolidate

此文章用于详细介绍malloc_consolidate。 众所周知&#xff0c;fastbin一般是不能合并&#xff0c;但在malloc_consolidate中是个例外。 1.触发机制 首先构造这样的堆块结构 一个0x40的堆块在fastbin中&#xff0c;一个0x110的堆块在unbin中 随后我们尝试分配一个0x300的堆…

智能BI(后端)-- 系统异步化

文章目录 系统问题分析什么是异步化&#xff1f;业务流程分析标准异步化的业务流程系统业务流程 线程池为什么需要线程池&#xff1f;线程池两种实现方式线程池的参数线程池的开发 项目异步化改造 系统问题分析 问题场景&#xff1a;调用的服务能力有限&#xff0c;或者接口的…

strcpy函数详解

strcpy函数详解 1.函数简介2.strcpy函数的使用2.1使用方法一2.1使用方法二 3.strcpy在使用过程中的注意事项3.1被复制字符必须以\0结尾3.2目标空间必须能够大于源字符串长度3.3目标空间必须可变 1.函数简介 strcpy函数包含在<string.h>库函数中&#xff0c;是将一个字符…

共享文件夹(以及问题解决方法)

目录 文件夹共享 第一步&#xff0c;将文件夹共享 第二步&#xff0c;设置用户权限 第三步&#xff0c;打开网络发现 第四步&#xff0c;访问 网络中没有设备问题 控制面板&#xff0c;启动 重启 还是不行&#xff1f;计算机管理&#xff0c;启动 FDResPub服务&#x…