四、归并排序(逆序对)

(一)、归并排序
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide
and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;
即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二
路归并。
例如有8 个数据需要排序:10 4 6 3 8 2 5 7
归并排序主要分两大步:分解、合并。
合并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元
素a[i]复制到r[k]中,并令i 和k 分别加上1;否则将第二个有序表中的元素a[j]复制
到r[k]中,并令j 和k 分别加上1,如此循环下去,直到其中一个有序表取完,然后再将
另一个有序表中剩余的元素复制到r 中从下标k 到下标t 的单元。归并排序的算法我们通
常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区
间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

#include //归并排序
#include
#include
#include
using namespace std;// 示例:7 5 8 15 8 45 2 5
int m[1001],r[1001];
void compare(int a,int b){
if(a==b) return;
int mid = (a+b) / 2;
compare(a,mid);
compare(mid+1,b);//这两行是我一直没有想通的,一百行for循环写的很嗨皮。。。
int qian=a,hou=mid+1,tot=a;
while(qian<=mid&&hou<=b){
if(m[qian]<=m[hou]){
r[tot] = m[qian];
tot++;
qian++;
}
else{
r[tot] = m[hou];
tot++;
hou++;
}
}
while(qian<=mid){
r[tot]=m[qian];
tot++;
qian++;
}
while(hou<=b){
r[tot]=m[hou];
tot++;
hou++;
}//这两个while不用再写if了,也比我的思路好。
for(int i=a;i<=b;i++){
m[i]=r[i];
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&m[i]);
}
compare(1,n);
for(int i=1;i<=n;i++){
printf("%d ",m[i]);
}
}

(二)、逆序对

上述提到归并排序是稳定的排序,相等的元素的顺序不会改变,进而用其可以解决逆序
对的问题。首先我们了解一下什么是逆序对。
逆序对:设A 为一个有n 个数字的有序集(n>1),其中所有数字各不相同。如果存
在正整数i, j 使得1 ≤ i < j ≤ n 而且A[i] > A[j],则<A[i], A[j]> 这个
有序对称为A 的一个逆序对,也称作逆序数。
例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4 个。
所谓逆序对的问题,即对给定的数组序列,求其逆序对的数量。
从逆序对定义上分析,逆序对就是数列中任意两个数满足大的在前,小的在后的组合。
如果将这些逆序对都调整成顺序(小的在前,大的在后),那么整个数列就变的有序,即排
序。因而,容易想到冒泡排序的机制正好是利用消除逆序来实现排序的,也就是说,交换相
邻两个逆序数,最终实现整个序列有序,那么交换的次数即为逆序对的数量。
冒泡排序可以解决逆序对问题,但是由于冒泡排序本身效率不高,时间复杂度为
O(n^2),对于n 比较大的情况就没用武之地了。我们可以这样认为,冒泡排序求逆序对效
率之所以低,是因为其在统计逆序对数量的时候是一对一对统计的,而对于范围为n 的序
列,逆序对数量最大可以是(n+1)*n/2,因此其效率太低。那怎样可以一下子统计多个,
而不是一个一个累加呢?这个时候,归并排序就可以帮我们来解决这个问题。
在合并操作中,我们假设左右两个区间元素为:
左边:{3 4 7 9} 右边:{1 5 8 10}
那么合并操作的第一步就是比较3 和1,然后将1 取出来,放到辅助数组中,这个时候
我们发现,右边的区间如果是当前比较的较小值,那么其会与左边剩余的数字产生逆序关系,
也就是说1 和3、4、7、9 都产生了逆序关系,我们可以一下子统计出有4 对逆序对。接
下来3,4 取下来放到辅助数组后,5 与左边剩下的7、9 产生了逆序关系,我们可以统计
出2 对。依此类推,8 与9 产生1 对,那么总共有4+2+1 对。这样统计的效率就会大大提
高,便可较好的解决逆序对问题。
而在算法的实现中,我们只需略微修改原有归并排序,当右边序列的元素为较小值时,
就统计其产生的逆序对数量,即可完成逆序对的统计。

#include //基本完全copy上一题
#include
#include
#include
using namespace std;// 示例:7 5 8 15 8 45 2 5
int m[1001],r[1001],number=0;
void compare(int a,int b){
if(a==b) return;
int mid = (a+b) / 2;
compare(a,mid);
compare(mid+1,b);//这两行是我一直没有想通的,一百行for循环写的很嗨皮。。。
int qian=a,hou=mid+1,tot=a;
while(qian<=mid&&hou<=b){
if(m[qian]<=m[hou]){
r[tot] = m[qian];
tot++;
qian++;
}
else{
r[tot] = m[hou];
tot++;
hou++;
number += mid-qian+1;//较上一题就加了这一行,一开始还直接无脑写成了++。。。
}
}
while(qian<=mid){
r[tot]=m[qian];
tot++;
qian++;
}
while(hou<=b){
r[tot]=m[hou];
tot++;
hou++;
}//这两个while不用再写if了,也比我的思路好。
for(int i=a;i<=b;i++){
m[i]=r[i];
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&m[i]);
}
compare(1,n);
for(int i=1;i<=n;i++){
printf("%d “,m[i]);
}
printf(”\n%d",number);
}

走楼梯

时间限制: 1 Sec 内存限制: 128 MB
题目描述
楼梯有N 级台阶,上楼可以一步上一阶,也可以一步上二阶,编一个递推程序,计算从第
一阶走到第N 阶共有多少种不同的方法。
输入
输入一个数N(1<=N<=10)
输出
输出共有多少种方法
样例输入
4
样例输出
5
解析:
这题是简单的递推题,首先,到第1 个台阶我们有1 种方法;到第2 个台阶,我们可
以在第0 个台阶上两阶,也可以在第1 个台阶上一阶到达,即有2 种方法;对于之后第i
个台阶来说,同样的方式,可以由第i-2 个台阶上两阶,也可以由第i-1 个台阶上一阶到
达。令f[i]表示到第i 台阶的方法数,由上述分析可以得出f[i]=f[i-1]+f[i-2],初
始f[1]=1,f[2]=2,即可推得到达第n 个台阶的方法数了,复杂度O(n)。

//两种方法差不多,有手就行

#include // 走楼梯(递归)
#include
#include
#include
using namespace std;
int jisuan(int n){
if(n1) return 1;
if (n2) return 2;
return jisuan(n-1)+jisuan(n-2);
}
int main(){
int n;
scanf("%d",&n);
printf("%d",jisuan(n));
}

#include // 走楼梯(递推)
#include
#include
#include
using namespace std;
int main(){
int n,m[101];
scanf("%d",&n);
m[1] = 1;
m[2] = 2;
for(int i=3;i<=n;i++){
m[i] = m[i-1]+m[i-2];
}
printf("%d",m[n]);
}

过河卒【NOIP2002】

时间限制: 1 Sec 内存限制: 128 MB
题目描述
棋盘上的A 点有一个过河卒,需要走到目标B 点。卒行走的规则是:可以向下、或者向右。
同时在棋盘上的任一点有一个对方的马(如C 点),对方的马所在的点和所有跳跃一步可
达的点称为对方马的控制点(如图中的C 点和P1,P2,…,P8)。卒不能通过对方马的控制
点。棋盘用坐标表示,A 点(0,0),B 点(n,m)(n,m 为不超过20 的整数),同样马的
位置坐标是需要给出的,C≠A 且C≠B。现在输入B 点和C 点的坐标,要你计算出过河卒
从A 点能够到达B 点的路径的条数。
图21 过河卒
输入
一行,4 个空格隔开的整数,表示(n,m)和C 点的坐标。
输出
过河卒从A 点能够到达B 点的路径的条数。
样例输入
4 8 2 4
样例输出
0
解析:
这题的递推思想和走楼梯的思路一样,在坐标(i,j)位置时,它可以由(i-1,j)和
(i,j-1) 点过来, 令f[i][j] 表示到达(i,j) 位置时的方案数, 那么
f[i][j]=f[i-1][j]+f[i][j-1]。当然此题稍微增加了难点,也就是有障碍点,这些
点不能达到,所以他们的方案数特殊处理为0,我们可以在一开始预处理掉。

#include // 过河卒 bug不少。。。
#include
#include
#include
using namespace std;
int main(){
int a[23][23],n,m,I,J,i,j;
scanf("%d%d%d%d",&n,&m,&I,&J);
for(i=0;i<=n;i++){
for(j=0;j<=m;j++){
a[i][j]=1;
printf("%d “,a[i][j]);
}
printf(”\n");
}
printf("\n");
if(I-1>=0&&J-2>=0) a[I-1][J-2]=0;
if(I-2>=0&&J-1>=0) a[I-2][J-1]=0;
if(I-2>=0&&J+1>=0) a[I-2][J+1]=0;
if(I-1>=0&&J+2>=0) a[I-1][J+2]=0;
if(I+1>=0&&J+2>=0) a[I+1][J+2]=0;
if(I+2>=0&&J+1>=0) a[I+2][J+1]=0;
if(I+1>=0&&J-2>=0) a[I+1][J-2]=0;
if(I+2>=0&&J-1>=0) a[I+2][J-1]=0;
a[I][J]=0;
for(i=1;i<=n;i++) if(a[i][0]) a[i][0]=a[i-1][0];
for(i=1;i<=m;i++) if(a[0][i]) a[0][i]=a[0][i-1];
for(i=0;i<=n;i++){
for(j=0;j<=m;j++){
printf("%d “,a[i][j]);
}
printf(”\n");
}
printf("\n");
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
if(a[i][j]){
a[i][j]=a[i-1][j]+a[i][j-1];
}
printf("%d “,a[i][j]);
}
printf(”\n");
}
printf("%d",a[n][m]);
}

震惊!递推与递归竟然可以这么编!%99的程序员都不知道!相关推荐

  1. 99%的程序员都在用Lombok,原理竟然这么简单?

    作者 | 老王 责编 | 郭芮 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手撸一个简易版的 Lombok,让你理解这个热 ...

  2. 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏

    世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手撸一个简易版的 ...

  3. 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!...

    世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手撸一个简易版的 ...

  4. 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!

    世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手撸一个简易版的 ...

  5. 0x02.基本算法 — 递推与递归

    目录 一.递推与递归 二.分治 三.模拟计算机实现递归 四.相应习题: 0.AcWing 92. 递归实现指数型枚举(递归/循环+位运算) 1.AcWing 93. 递归实现组合型枚举 2.AcWin ...

  6. 算法设计与分析 1 递推与递归

    递推与递归 Fibonacci #include<stdio.h> #pragma warning (disabled:4996) #define MAX 100 int f[MAX];/ ...

  7. 基本算法之递推与递归的简单应用

    递推与递归的简单应用 常见的枚举形式 实现指数型枚举 DFS (一) DFS (二) 位运算(一) 位运算(二) 实现组合型枚举 DFS + 剪枝 实现排列型枚举 DFS 费解的开关 奇怪的汉诺塔 分 ...

  8. 算法总结之递推与递归

    递推算法 递归算法大致包括两方面的内容:1)递归起点 : 2)递归关系 递推起点 递归起点一般由题目或者实际情况确定,不由递归关系推出.如果无法确定递归起点,那么递归算法就无法实现.可见,递归起点是递 ...

  9. 递推与递归 (区别)

    递推与递归 本文中部分内容转自他人博客,作者相关信息以及博客地址在文末. 概念 递归:从已知问题的结果出发,用迭代表达式逐步推算出问题的开始的条件,即顺推法的逆过程,称为递归. 递归的定义:在一个函数 ...

最新文章

  1. Android的EditText自动获取焦点并弹出输入法问题
  2. java方块排序_[代码全屏查看]-NxN方块排序,可自动运行
  3. 高级数据结构---优先队列
  4. Jquery和雅虎的YQL服务实现天气预报功能!
  5. UIWebView / NSURL / NSBoundle 相关应用 (实例,加载完成前的背景, 默认safari打开链接地址等)...
  6. 大数据之_SCALA工作笔记001---Centos7.3安装scala
  7. 将之前写完的猜数字游戏改为通过javabean_【沃德英语】好玩的单词游戏
  8. 12. Integer to Roman
  9. Audio Jungle宣传专题片头音乐AE模板专用配乐合集-永久更新
  10. 《通信原理》复习笔记4----第四章信道相关例题
  11. 淘宝客佣金设置多少合适?淘宝客的佣金是怎么算的?
  12. git status 命令详解
  13. Windows 2000进程细述.
  14. python资格证_掌握核心竞争力:五大数据科学类资格证书
  15. JPA映射数据库mysql表名,字段名大小写转化,下划线分割.
  16. Ubuntu安装luminati
  17. 2012淘宝关键字优化淘宝关键字SEO优化助手|淘宝关键字SEO优化
  18. 从零开始学区块链dapp开发之remix安装
  19. 基于java电影票预订管理系统设计与实现
  20. php正则匹配input,正则表达式 - php正则匹配p标签及带特定的中文

热门文章

  1. c#语言输出字符串长度,根据宽度来决定显示的字符串长度(C#)
  2. pythonopencv显示图像_OpenCV-Python 读取显示图像 | 五
  3. 计算机专业的双证在职研究生,计算机类在职研究生最终能获得双证吗难度是不是很大呢...
  4. 最近有粉丝向我请教Java泛型,于是重温了一下,希望学弟面试顺利
  5. compress后的bytearray再decode变大_笔记本电脑风扇噪音变大的原因及其解决办法
  6. 攻城时服务器维护,8月31日服务器维护更新公告
  7. php restful规范,RESTFul API规范 详细指南
  8. mysql怎么访问用户B_MySQL访问控制和用户管理
  9. 用户登录查全表好还是用用户名好_外贸人/货代人不要为海运难过了:请看如何查运价和调配舱位解决缺箱!...
  10. 『软件测试3』八大典型的黑盒测试方法已来袭,快快接住!