归并排序(Merge_Sort)
基本思想
建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
算法原理
归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针到达序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
特点
数据结构:数组
稳定性:稳定
过程
https://blog.csdn.net/weixin_43272781/article/details/85013461
排序过程 宏观过程
复杂度与辅助空间
最差时间复杂度:O(nlogn)
最优时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
所需辅助空间:O(n)
源程序
//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + ;
int m = mid, n = last;
int k = ; while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
} while (i <= m)
temp[k++] = a[i++]; while (j <= n)
temp[k++] = a[j++]; for (i = ; i < k; i++)
a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / ;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + , last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
} bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, , n - , p);
delete[] p;
return true;
}
一、圆周率π计算
- /*
- *@Author: STZG
- *@Language: C++
- */
- #include <bits/stdc++.h>
- using namespace std;
- long a=10000,b,c=56000,d,e,f[56001],g;
- int main(){
- for(;b-c ; )f[b++]=a/5;
- for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)
- for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);
- //cout << "Hello world!" << endl;
- return 0;
- }
二、数学公式
π = 2 + 1/3 * (2 + 2/5 * (2 + 3/7 * (2 + ... (2 + k/2k+1 * (2 + ... ))...)))
三、分析
- #include <stdio.h>
- int a=10000,b,c=2800,d,e,f[2801],g;
- main() {
- int i;
- for(i=0;i<c;i++)
- f[i]=a/5;
- while(c!=0)
- {
- d=0;
- g=c*2;
- b=c;
- while(1)
- {
- d=d+f[b]*a;
- g--;
- f[b]=d%g;
- d=d/g;
- g--;
- b--;
- if(b==0) break;
- d=d*b;
- }
- c=c-14;
- printf("%.4d",e+d/a);
- e=d%a;
- }
- }
要想计算出无限精度的PI,我们需要上述的迭代公式运行无数次,并且其中每个分数也是完全精确的,这在计算机中自然是无法实现的。那么基本实现思想就是迭代足够多次,并且每个分数也足够精确,这样就能够计算出PI的前n位来。上面这个程序计算800位,迭代公式一共迭代2800次。
int a=10000,b,c=2800,d,e,f[2801],g;
这句话中的2800就是迭代次数。
由于float或者double的精度远远不够,因此程序中使用整数类型(实际是长整型),分段运算(每次计算4位)。我们可以看到输出语句printf("%.4d",e+d/a); 其中%.4就是把计算出来的4位输出,我们看到c每次减少14( c=c-14;),而c的初始大小为2800,因此一共就分了200段运算,并且每次输出4位,所以一共输出了800位。
由于使用整型数运算,因此有必要乘上一个系数,在这个程序中系数为1000,也就是说,公式如下:
1000*pi = 2K+ 1/3 * (2K+ 2/5 * (2K+ 3/7 * (2K+ ... (2K+ k/2k+1 * (2K+ ... ))...)))
这里的2K表示2000,也就是f[2801]数组初始化以后的数据,a=10000,a/5=2000,所以下面的程序把f中的每个元素都赋值为2000:
for(i=0;i<c;i++)
f[i]=a/5;
你可能会觉得奇怪,为什么这里要把一个常数储存到数组中去,请继续往下看。我们先来跟踪一下程序的运行:
- while(c!=0) //假设这是第一次运行,c=2800,为迭代次数
- {
- d=0;
- g=c*2; //这里的g是用来做k/(2k+1)中的分子
- b=c; //这里的b是用来做k/(2k+1)中的分子
- while(1)
- while(1)
- {
- d=d+f[b]*a; //f中的所有的值都为2000,这里在计算时又把系数扩大了a=10000倍。这样做的目的稍候介绍,你可以看到输出的时候是d/a,所以这不影响计算
- g--;
- f[b]=d%g; //先不管这一行
- d=d/g; //第一次运行的g为2*2799+1,你可以看到g做了分母
- g--;
- b--;
- if(b==0) break;
- d=d*b; //这里的b为2799,可以看到b做了分子。
- }
- c=c-14;
- printf("%.4d",e+d/a);
- e=d%a;
- }
只需要粗略的看看上面的程序,我们就大概知道它的确是使用的那个迭代公式来计算Pi的了,不过不知道到现在为止你是否明白了f数组的用处。如果没有明白,请继续阅读。
d=d/g,这一行的目的是除以2k+1,我们知道之所以程序无法精确计算的原因就是这个除法。即使用浮点数,答案也是不够精确的,因此直接用来计算800位的Pi是不可能的。那么不精确的成分在哪里?很明显:就是那个余数d%g。程序用f数组把这个误差储存起来,在下次计算的时候使用。现在你也应该知道为什么d=d+f[b]*a;中间需要乘上a了吧。把分子扩大之后,才好把误差精确的算出来。
d如果不乘10000这个系数,则其值为2000,那么运行d=d/g;则是2000/(2*2799+1),这种整数的除法答案为0,根本无法迭代下去了。
现在我们知道程序就是把余数储存起来,作为下次迭代的时候的参数,那么为什么这么做就可以使得下次迭代出来的结果为接下来的数字呢?
这实际上和我们在纸上作除法很类似:
0142
/———
7/ 1
10
7
——————
30
28
——————
20
14
——————
6
.....
我们可以发现,在做除法的时候,我们通常把余数扩大之后再来计算,f中既然储存的是余数,而f[b]*a;则正好把这个余数扩大了a倍,然后如此循环下去,可以计算到任意精度。
这里要说明的是,事实上每次计算出来的d并不一定只有4位数,例如第一次计算的时候,d的值为31415926,输出4位时候,把低四位的值储存在e中间,e=d%a,也就是5926。
最后,这个c=c-14不太好理解。事实上没有这条语句,程序计算出来的仍然正确。只是因为如果迭代2800次,无论分数如何精确,最后Pi的精度只能够达到800。
你可以把程序改为如下形式尝试一下:
- for(i=0;i<800;i++)
- {
- {
- d=0;
- g=c*2;
- b=c;
- while(1)
- {
- d=d+f[b]*a;
- g--;
- f[b]=d%g;
- d=d/g;
- g--;
- b--;
- if(b==0) break;
- d=d*b;
- }
- //c=c-14; //不要这句话。
- printf("%.4d",e+d/a);
- e=d%a;
- }
最后的答案仍然正确。
不过我们可以看到内循环的次数是c次,也就是说每次迭代计算c次。而每次计算后续位数的时候,迭代次数减少14,而不影响精度。
归并排序(Merge_Sort)的更多相关文章
- 疯子的算法总结(六) 复杂排序算法 ① 归并排序 merge_sort()
归并排序采取了分治的思想,每次分别排左半边和右半边,不断递归调用自己,直到只有一个元素递归结束,开始回溯,调用merge函数,合并两个有序序列,再合并的时候每次给末尾追上一个最大int这样就不怕最后一 ...
- 归并排序merge_sort
将区间递归分解,直到区间只有2个元素,然后比较大小,排序,等递归回来的时候就将排序好的子区间再排序合并....一直排序合并,最后就排序完成了. (可以做范围大的逆序数的题) #include < ...
- 【Algorithm】自顶向下的归并排序
一. 算法描述 自顶向下的归并排序:采用分治法进行自顶向下的程序设计方式,分治法的核心思想就是分解.求解.合并. 先将长度为N的无序序列分割平均分割为两段 然后分别对前半段进行归并排序.后半段进行归并 ...
- Open Data Structure Templates
数据结构模板 Chen 2016/12/22 前言 本篇博客的模板,全部是我纯手打的,如果有发现错误,请在下方留言指正:).欢迎大家参考. 有一些地方还不是很完善,等过一阵子用C++实现和部分重构下. ...
- 给一已经排序数组A和x,求A中是否包含两个元素之和为x
亲爱的大神老爷们,这是小渣第一次写blog,欢迎大家来喷(批评指正),算法渣因为面试中连这道题都不会写就自己琢磨了一下,也参考了网上大家的做法 解法一: 思路:从首尾向目的靠拢,因为已经排序,[假设存 ...
- JAVA:数组,排序,查找<4>
一.数组 1.一维数组 (1).数组的定义 数据类型 数组名[]=new 数据类型[大小] public class Demo1 { public static void main(String[] ...
- Java学习第四篇:数组,排序,查找
一.数组 1.一维数组 (1).数组的定义 数据类型 数组名[]=new 数据类型[大小] public class Demo1 { public static void main(String[] ...
- ###Maintainable C++
点击查看Evernote原文. #@author: gr #@date: 2014-08-15 #@email: forgerui@gmail.com 记录一些标准规范.让自己的编码更可读,更可维护. ...
- MERGE_SORT归并排序C++实现
大家好,我是小鸭酱,博客地址为:http://www.cnblogs.com/xiaoyajiang 以下实现归并排序,第一部分含有哨兵(算法来自<算法导论>),第二部分不含哨兵 第一部分 ...
随机推荐
- BZOJ 4765(分块+树状数组)
题面 传送门 "奋战三星期,造台计算机".小G响应号召,花了三小时造了台普通计算姬.普通计算姬比普通计算机要厉害一些 .普通计算机能计算数列区间和,而普通计算姬能计算树中子树和.更 ...
- MYSQL 查询脚本优化
业务需要,优化一段多表查询脚本. 总结下来,采取以下步骤. 分析语句 分析语句,了解逻辑,是否可以先优化逻辑. 查询语句的查询范围,是否是全表查询,如果是,尽量优化为按索引查询. 查看语句数量,是否有 ...
- Juqery插件编写 基础说明
<!DOCTYPE html> <html> <head> <meta name="viewport" content="wid ...
- 从vue的组件传值着手浅谈观察者模式
首先,提到观察者模式,这不禁让我想到了MVVM,MVVM架构模式感觉用到了观察者的思想. 我们还是按照惯例,了解一下什么是观察者模式 观察者模式,类似发布订阅模式,完成这个动作首先最少得有两个不同的对 ...
- XMPP即时通讯协议使用(十)——好友关系状态
sub ask recv 订阅 询问 接受 含义 substatus -1- 应该删除这个好友 Indicates that the roster item should be ...
- shutdown - 关闭系统
总览 SYNOPSIS /sbin/shutdown [-t sec] [-arkhncfF] time [warning-message] 描述 DESCRIPTION shutdown 以一种安全 ...
- linux随笔-03
必须掌握的Linux命令 系统状态检测命令 1.ifconfig命令 ifconfig命令用于获取网卡配置与网络状态等信息,格式为“ifconfig [网络设备] [参数]”. 使用ifconfig命 ...
- Dubbox管理中心的部署及使用
安装: 我们在开发时,需要知道注册中心都注册了哪些服务,以便我们开发和测试.我们可以通过部署一个管理中心来实现.其实管理中心就是一个web应用,部署到tomcat即可. (1)编译源码,得到war包 ...
- Java集合类里面最基本的接口
Collection:代表一组对象,每一个对象都是它的子元素. Set:不包含重复元素的Collection. List:有顺序的Collection,并且可以包含重复元素. Map:可以把键(key ...
- JQuery-跑马灯(文字无缝向上翻动-封装)
转载自他人:https://blog.csdn.net/z69183787/article/details/12857587 (function($){ $.fn.extend({ &qu ...