经典排序算法 — C#版本(中)
归并排序比较适合大规模得数据排序,借鉴了分治思想。
归并排序原理

自古以来,分久必合合久必分。
我们可以这样理解归并排序,分-分到不能分为止,然后合并。
使用递归将问题一点一点分解,最后进行合并。
分而治之 (merge_sort)
提到递推,我们使用地递推解决问题,首先要分析出递推公式、明确结束条件。
递推公式: merge_sort(i...n)=merge( merge_sort(i...j), merge_sort(j+...n) ) 结束条件:
i>=n
分久必合(merge)
将两个有序的数组进行合并,这样整个数组也就是排序好的数组了。
那么怎么进行合并呢?-- (i...j) 和 (j+1...n) 重新排序后,重新放入原来的数组 (i...n)

两组数组 [3, 8, 9, 11] vs [1, 2, 5, 7]
两个游标 蓝色 和 红色
3>1,1小,1入新数组,红色游标后移一位,继续比较...

3>2,2小,2入数组,红色游标后移一位

3<5,3小,3入数组,蓝色游标后移一位

8>5,5小,5入数组,红色游标后移一位

8>7,7小,7入数组,红色游标后移,右侧数组全部转移完毕

当有一组数组全部转移完毕,那么剩下的一组中的全部元素依次转入到新数组中,新数组正式成为一个有顺序的数组

通过以上两点:递推公式和合并思想,我们使用代码实现一下:
1、如下图:递归方式 进行分解,然后使用合并代码进行合并。
/// <summary>
/// 递归调用
/// </summary>
/// <param name="a">原始数组</param>
/// <param name="p">分割点</param>
/// <param name="r">结束位置</param>
public static void MergrSortInternally(int[] a, int p, int r)
{
//结束条件
if (p >= r)
return; //切割点
int q = p + (r - p) / ; //分而治之
MergrSortInternally(a, p, q); MergrSortInternally(a, q + , r); //合并 A(a, p, q) 和 A(a, q + 1, r)
Merage(a, p, q, r); }

2、我们再来看看合并逻辑
参数:原始数组,开始的地方,切割的地方,结束的地方
逻辑:两个切割数组的各自的游标
申请同样大小的临时数组
循环比较;小的入临时,游标后移;知道有一个数组空了为止
找到剩下不为空的那个数组,将剩余元素入临时
将临时数组,找到原始数组的对应为止进行覆盖
/// <summary>
/// 合并
/// </summary>
/// <param name="a">原始数组</param>
/// <param name="p">起始点</param>
/// <param name="q">切割点</param>
/// <param name="r">结束点</param>
public static void Merage(int[] a, int p, int q, int r)
{
// i 和 j = 两个数组的游标
int i = p;
int j = q + ; // 临时数组的游标
int k = ; // 临时数组
int[] temp = new int[r - p + ]; //最小入队,直到其中一个空空如也为止
while (i <= q && j <= r)
{
if (a[i] <= a[j])
{
temp[k] = a[i];
++k;
++i;
}
else
{
temp[k] = a[j];
++k;
++j;
}
} // 找到另一个不为空的,找到剩下的元素
int start = i;
int end = q; if (j <= r)
{
start = j;
end = r;
} // 剩余数组拷贝到临时数组 temp
while (start <= end)
{
temp[k++] = a[start++];
} // 将temp覆盖到a[p...r]
for (i = ; i <= r - p; ++i)
{
a[p + i] = temp[i];
}
}


归并排序性能分析
Q:是不是稳定排序?
A:是
对于这两组数组 A[p...q] 和 A[q+1...r] 来说
代码中也是这样实现的,a[i]就是左侧数组,a[j]就是右侧数组,保证相等时左侧优先入队即可。注意 等号位置。

Q:是否是原地排序?
A:当然不是
因为我们在合并代码时候,申请了同样大小的内存空间。
但是对于这里的归并排序的空间复杂度又是多少呢?
虽然牵扯到了递归,但是临时变量这里会在一个函数结束后栈会释放,所以空间复杂度是O(n)
Q:时间复杂度又是多少呢?
A:O(n log n)
我们对 n 个元素的归并排序时间记作 T(n),
分解函数分解两个子数组的时间是T(n/2)
合并函数时间复杂度是O(n)
T(1)=C; n=1
T(n)=2*T(n/2)+ n; n>1
T(n) = 2*T(n/2) + n
= 2*(2*T(n/4) + n/2) + n = 4*T(n/4) + 2*n
= 4*(2*T(n/8) + n/4) + 2*n = 8*T(n/8) + 3*n
= 8*(2*T(n/16) + n/8) + 3*n = 16*T(n/16) + 4*n
......
= 2^k * T(n/2^k) + k * n
T(n) = 2^k * T(n/2^k) + k * n
当 T(n/2^k) = T(1)=> k = log2 n
即:T(n) = Cn + n log2 n => O(n log n)
经典排序算法 — C#版本(中)的更多相关文章
- 经典排序算法总结与实现 ---python
原文:http://wuchong.me/blog/2014/02/09/algorithm-sort-summary/ 经典排序算法在面试中占有很大的比重,也是基础,为了未雨绸缪,在寒假里整理并用P ...
- 【最全】经典排序算法(C语言)
算法复杂度比较: 算法分类 一.直接插入排序 一个插入排序是另一种简单排序,它的思路是:每次从未排好的序列中选出第一个元素插入到已排好的序列中. 它的算法步骤可以大致归纳如下: 从未排好的序列中拿出首 ...
- python 经典排序算法
python 经典排序算法 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存.常见的内部排序算 ...
- 十大经典排序算法最强总结(含JAVA代码实现)(转)
十大经典排序算法最强总结(含JAVA代码实现) 最近几天在研究排序算法,看了很多博客,发现网上有的文章中对排序算法解释的并不是很透彻,而且有很多代码都是错误的,例如有的文章中在“桶排序”算法中对每 ...
- 十大经典排序算法最强总结(含Java、Python码实现)
引言 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法.排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面 ...
- 十大经典排序算法(java实现、配图解,附源码)
前言: 本文章主要是讲解我个人在学习Java开发环境的排序算法时做的一些准备,以及个人的心得体会,汇集成本篇文章,作为自己对排序算法理解的总结与笔记. 内容主要是关于十大经典排序算法的简介.原理.动静 ...
- python基础__十大经典排序算法
用Python实现十大经典排序算法! 排序算法是<数据结构与算法>中最基本的算法之一.排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大, ...
- 经典排序算法 – 插入排序Insertion sort
经典排序算法 – 插入排序Insertion sort 插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕. 插入排序方法分直接插入排序和折半插入排序两种, ...
- 经典排序算法及python实现
今天我们来谈谈几种经典排序算法,然后用python来实现,最后通过数据来比较几个算法时间 选择排序 选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理是每一次从待排序的数据 ...
随机推荐
- java编程思想-第六章-某些练习题
参考https://blog.csdn.net/caroline_wendy/article/details/47271037 3 package debug; import java.util.Ar ...
- Java注解Retention、Documented、Target的含义
Retention注解 Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值: 1.RetentionPolicy.SOURCE -- 这种类型的Annotations只在 ...
- privoxy自动请求转发到多个网络
有些时候我们需要通过不同的代理访问不同资源,比如某些ip或域名走本地网络,某些ip或域名走不可描述的代理等.当然这只是举个栗子! 我要解决的问题是:我的内网机器没有internet访问权限,但是我的应 ...
- C++ bitset用法
概念: bitset是用来存储位的(其中的元素只有两种形式) 这个类通常用来模拟一个布尔数组,但对空间分配上进行了优化:通常,每个元素只占用一个bit ,而通常char类型是它的八倍 每个位置上的位都 ...
- python环境下实现OrangePi Zero寄存器访问及GPIO控制
最近入手OrangePi Zero一块,程序上需要使用板子上自带的LED灯,在网上一查,不得不说OPi的支持跟树莓派无法相比.自己摸索了一下,实现简单的GPIO控制方法,作者的Zero安装的是Armb ...
- java的8种基础类型
一.基础类型 Java 是一种强类型语言 . 这就意味着必须为每一个变量声明一种类型 : 在 Java 中,一共有 8种基本类型 ( primitive type ) , 其中有 4 种整型 . 2 ...
- 读书笔记:深入理解java虚拟机(二)创建对象的时候需要访问哪几块内存
@TOC 对象在内存中如何储存 对象访问在java语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会涉及到java栈,java堆,方法去三个最重要的内存区域的关联关系,比如下面这段代码: ...
- linux安装nvm node版本管理器 nvm常用命令 部署node服务器环境
1,nvm git地址点击打开链接,安装命令 curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh ...
- Haskell学习-函数式编程初探
原文地址:Haskell学习-函数式编程初探 为什么要学习函数式编程?为什么要学习Haskell? .net到前端,C#和JavaScript对我来说如果谈不上精通,最起码也算是到了非常熟悉的 ...
- Python:读取 .doc、.docx 两种 Word 文件简述及“Word 未能引发事件”错误
概述 Python 中可以读取 word 文件的库有 python-docx 和 pywin32. 下表比较了各自的优缺点. 优点 缺点 python-docx 跨平台 只能处理 .docx 格式 ...