笔试算法题(32):归并算法求逆序对 & 将数组元素转换为数组中剩下的其他元素的乘积

出题:多人按照从低到高排成一个前后队列,如果前面的人比后面的高就认为是一个错误对;
  例如:[176,178,180,170,171]中的错误对

    <176,170>, <176,171>, <178,170>, <178,171>,
< 180,170>, <180,171>。
  现在要求从一个整数序列中找出所有这样的错误对;

分析:

  • 逆序对(Inversion
    Pair):在N个可判断大小的数中,逆序对的数量为[0,n(n-1)/2],使用归并排序求一个序列的逆序对数量,时间复杂度为O(NlogN),空
    间复杂度为O(N);

  • 使用m=(i+j)/2递归处理数字序列,首先计算小子文件的逆序对,并进行排序;排序之后的小子文件参与大文件的逆序对求取,由于
    已经小子文件已经排序,所以可以避免许多比较操作;

解题:


 1 int Partition(int *array, int i, int j) {
2 /**
3 * 使用额外O(N)的空间保存最终排序的序列
4 * */
5 int m=(i+j)/2;
6 int tarray[j-i+1]; int index=0;
7 int ti=i,tj=m+1;
8 int count=0;
9
10 printf("\n%d,%d, %d,%d",i,j,array[i],array[j]);
11 while(ti<=m && tj<=j) {
12 if(array[ti]>array[tj]) {
13 /**
14 * 注意仅当右边序列的元素小于左边序列
15 * 的元素时,count的值才会增加,并且根据
16 * 以排序的特性可得出总计的逆序对
17 * */
18 count+=m-ti+1;
19 tarray[index]=array[tj];
20 tj++;
21 } else {
22 tarray[index]=array[ti];
23 ti++;
24 }
25 index++;
26 }
27 /**
28 * 注意处理当左右子序列的剩余元素,由于已经排序,所以
29 * 可以直接复制到tarray中
30 * */
31 if(ti>m) {
32 while(tj<=j) {
33 tarray[index]=array[tj];
34 tj++;index++;
35 }
36
37 } else if(tj>j) {
38 while(ti<=m) {
39 tarray[index]=array[ti];
40 ti++;index++;
41 }
42 }
43
44 for(int k=i;k<=j;k++)
45 array[k]=tarray[k];
46
47 return count;
48 }
49
50 int Merge(int *array, int i, int j) {
51 /**
52 * 当只有一个元素的时候,返回0
53 * 当i和j相邻的时候,使用直接比较替代递归调用
54 * */
55 printf("\n**%d, %d",i,j);
56 if(i==j) return 0;
57 if(i+1==j) {
58 if(array[i]>array[j]) {
59 int t=array[i];
60 array[i]=array[j];
61 array[j]=t;
62 return 1;
63 } else
64 return 0;
65 }
66
67 /**
68 * 使用二分递归,count的值由三部分决定:
69 * 左右子序列各自内部的逆序对,和左子序列和
70 * 右子序列之间的逆序对。
71 * 由于经过Merge之后左右子序列已经排序,所以
72 * partition可以在O(N)时间复杂度内完成,但是
73 * 需要额外的O(N)的空间复杂度
74 * */
75 int m=(i+j)/2;
76 int count=0;
77 count+=Merge(array,i,m);
78 count+=Merge(array,m+1,j);
79 count+=Partition(array,i,j);
80
81 return count;
82 }
83
84 int main() {
85 int array[]={7,2,1,4,3,5,6};
86 printf("\n%d",Merge(array, 0, 6));
87
88 return 0;
89 }

出题:一个长度为n的数组a[0],a[1],...,a[n-1]。现在更新数组的名个元素,即a[0]变为a[1]到a[n-1]的积,a[1]变为
a[0]和a[2]到a[n-1]的积,...,a[n-1]为a[0]到a[n-2]的积(就是除掉当前元素,其他所有元素的积);
  1). 要求具有线性复杂度;
  2). 要求不能使用除法运算;

分析:创建两个数组left[N]和right[N],对于a[i]而言,left[i]存储i之前的元素乘积,right[i]存储i之后的元素乘积,所以left和right的初始化仅需要两次扫描数组,为线性时间复杂度,并且没有使用除法;

解题:


 1 void Transfer(int *array, int length) {
2 int leftarray[length];
3 int rightarray[length];
4
5 leftarray[0]=1;
6 for(int i=1;i<length;i++)
7 leftarray[i]=leftarray[i-1]*array[i-1];
8
9 rightarray[length-1]=1;
10 for(int i=length-2;i>-1;i--)
11 rightarray[i]=rightarray[i+1]*array[i+1];
12
13 for(int i=0;i<length;i++)
14 array[i]=leftarray[i]*rightarray[i];
15 }
16
17 int main() {
18 int array[]={5,2,3,4};
19 int length=4;
20 Transfer(array,length);
21 for(int i=0;i<length;i++)
22 printf("%d, ",array[i]);
23 return 0;
24 }

时间: 2024-10-07 17:43:09

笔试算法题(32):归并算法求逆序对 & 将数组元素转换为数组中剩下的其他元素的乘积的相关文章

算法笔记_065:分治法求逆序对(Java)

目录 1 问题描述 2 解决方案 2.1 蛮力法 2.2 分治法(归并排序)   1 问题描述 给定一个随机数数组,求取这个数组中的逆序对总个数.要求时间效率尽可能高. 那么,何为逆序对? 引用自百度百科: 设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同. 如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数. 例如,数组(3,1,4,5,

归并法求逆序对

l例题连接:https://www.luogu.org/problem/P1908 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计.最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对.知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目.Update:数据已加强. 输入格式 第一行,一个数n,表示序列中有n个数. 第

codevs 4163 求逆序对的数目 -树状数组法

4163 hzwer与逆序对 时间限制: 10 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题目描述 Description hzwer在研究逆序对. 对于数列{a},如果有序数对(I,j)满足:i<j,a[i]>a[j],则(i,j)是一对逆序对. 给定一个数列{a},求逆序对个数. 输入数据较大,请使用scanf代替cin读入. 输入描述 Input Description 第一行一个数n,表示{a}有n个元素. 接下来n个数,描述{a}. 输出描述 Output D

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少? 因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了. 因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的: 线段树,树状数

【bzoj2789】[Poi2012]Letters 树状数组求逆序对

题目描述 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. 输入 第一行一个正整数n (2<=n<=1,000,000),表示字符串的长度. 第二行和第三行各一个长度为n的字符串,并且只包含大写英文字母. 输出 一个非负整数,表示最少的交换次数. 样例输入 3 ABC BCA 样例输出 2 题解 树状数组求逆序对 一个结论:将序列A通过交换相邻元素变换为序列B,需要的最小次数为A中

复习---归并排序求逆序对--计蒜客2017noip模拟赛二--蒜头君的排序

题目链接:https://nanti.jisuanke.com/t/16443 我不会矩阵快速幂,所以只拿了60分, 发现归并排序掌握的并不熟练,借此良机复习一下. 重在归并排序分治思想,要牢记! #include<iostream> #include<cstring> using namespace std; int n,m,a[30005],s[30005],ans,d[30005]; void msort(int l,int r) { if(l==r)return;//如果只

算法题:整数排成一排求最大值

算法题:整数排成一排求最大值 题目描述 [编程题]丈母娘的考验 题目ID:1136 [问题描述]小豪和女友恋爱数年,终于要修成正果了.今天,他将以准女婿的身份去女友家,唉呀,忐忑不安. 丈母娘一见小豪,嗯嗯,果然是传说中的高富帅,不错不错! 额额额,等会,这孩子不会是红漆马桶吧?长得好看,可是一肚子的粑粑o(^▽^)o,那可配不上我家宝贝乖乖女儿哦. "小豪,好孩子,能帮阿姨一个忙吗?老年大学有一道题,我还不会呢." 原来是这样的一道题:有三个正整数,将其连成一排,求最大的数. 例如,

51 Nod 1107 斜率小于0的连线数量 (转换为归并求逆序数或者直接树状数组,超级详细题解!!!)

1107 斜率小于0的连线数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 二维平面上N个点之间共有C(n,2)条连线.求这C(n,2)条线中斜率小于0的线的数量. 二维平面上的一个点,根据对应的X Y坐标可以表示为(X,Y).例如:(2,3) (3,4) (1,5) (4,6),其中(1,5)同(2,3)(3,4)的连线斜率 < 0,因此斜率小于0的连线数量为2. Input 第1行:1个数N,N为点的数量(0 <= N <= 50000) 第2

每日一题26:求逆序对数目与求和

求逆序对问题与解决方案原理 在一个数列中,如果规定从小到大为正序,那么如果排在后面的某个元素比前面的某一个元素小,那么就称这两个数构成一个逆序对,例如,数列5,4,3,2,1中,任一个数都与它前面的数构成逆序对,这个序列中一共就有1+2+3+4=10个逆序对,数列23541中有5个逆序对,那么任给定一个数列,如何知道有多少个逆序对呢?简单的方法是将每个元素与它后面的元素比较,然后就可以累加出总的逆序对数目,这种算法的复杂度是O(n^2).另一种比较快的方法是利用归并排序,得到的算法复杂度为O(n

2014 HDU多校弟五场A题 【归并排序求逆序对】

这题是2Y,第一次WA贡献给了没有long long 的答案QAQ 题意不难理解,解题方法不难. 先用归并排序求出原串中逆序对的个数然后拿来减去k即可,如果答案小于0,则取0 学习了归并排序求逆序对的方法,可以拿来当模板 TVT 贴代码了: 1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <math.h> 5 #include <iostream&g