逆序对-树状数组or暴力-python

简介: 逆序对-树状数组or暴力-python

SORT 公司是一个专门提供排序服务的公司,该公司的宗旨是:“顺序是美丽的”。他们的工作是通过一系列移动,将某些物品按顺序摆好。他们的服务是通过工作量来计算的,即移动物品的次数。所以,在工作前必须先考察工作量,以便向客户提出收费数目。

用户并不需要知道精确的移动次数,实质上,大多数人都是凭感觉来认定这一列物品的混乱程度。根据 SORTSORT 公司的经验,人们一般是根据“逆序对”的数目多少来称呼这一序列的混乱程度。假设将序列中第I件物品的参数定义为 Ai,那么排序就是将 A1,⋯An 从小到大排序。

若 i<j 且 Ai>Aj,则 <i,j>就为一个“逆序对".

SORT 公司请你写一个程序,在尽量短的时间内统计出”逆序对“的数目


运行限制


  • 最大运行时间:1s
  • 最大运行内存: 128M

思路:

暴力解法:


根据定义,我们遍历数组,如果发现当前的数字后面存在比他大的数,则计数器++。代码复杂度o(n^2)


代码


1. c = 0
2. b = input()
3. a = list(map(int,input().split()))
4. for i in range(0,len(a) - 1):
5. for j in range(i + 1,len(a)):
6. if a[i] > a[j]:
7.       c += 1
8. print(c)

树状数组


有关这道题的树状数组的解法可以先参考这位博主的文章,讲的很详细。大佬的思路

树状数组是一种用来求前缀和的时间复杂度比较低的数据结构,我们可以通过树状数组讲问题”“改变 n次元素值,查询 n 次区间和”的总复杂度降为 O(nlogn)。


右图圆圈中标记有数字的节点,存储的是称为树状数组的tree[],一个结点上的tree[]的值,就是它树下的直连子节点的和,例如:

  • tree[1]=a1
  • tree[2]=tree[1]+a2
  • tree[3]=a3
  • tree[4]=tree[2]+tree[3]+a4
  • tree[8]=tree[4]+tree[6]+tree[7]+a8

我们利用tree【】,可以高效的完成下面两个操作:

  1. 查询,即求前缀和 sumsum,例如:
  • sum(8)=tree[8]
  • sum(7) = tree[7] + tree[6] + tree[4]
  • sum(6) = tree[6] + tree[4]
  1. 右图中的虚线箭头是计算 sum(7) 的过程。显然,计算的复杂度是 O(logn) 的,这样就达到了快速计算前缀和的目的。
  2. 维护。tree[]本身的维护也是高效的。当元素 a 发生改变时,能以 O(logn) 的高效率修改 tree[] 的值。例如更新了a3,那么只需要修改 tree[3]、tree[4]、tree[8]⋯,即修改它和它上面的那些结点:父结点以及父结点的父结点。

至于如何找到下一个节点,我们通过下面的lowbit()去寻找

下面我们介绍四个函数


lowbit(x):


找到一个数的二进制的最后一个1,为什么要找最后一个1呢?因为我们观察维护和查询这两个操作时,不难发现:

1.在查询的过程中,是每次去掉二进制的最后的1。例如求 sum(7) = tree[7] + tree[6] + tree[4],步骤是:

  • 7 的二进制是 111,去掉最后的 1,得 110,即 tree[6];
  • 去掉 6 的二进制 110 的最后一个 11,得 100,即 tree[4];
  • 4 的二进制是 100,去掉 1 之后就没有了。

2.在维护的过程中,是每次在二进制的最后的 1 上加 1。例如更新了 a3,需要修改 tree[3]、tree[4]、tree[8]\cdotstree[8]⋯ 等等,步骤是:

  • 3 的二进制是 11,在最后的 1 上加上 1 得 100 ,即 44 ,修改 tree[4];
  • 4 的二进制是 100,在最后的 1 上加 1,得 1000,即 8,修改 tree[8];
  • 继续修改 tree[16]、tree[32]⋯ 等等。

而我们的lowbit()就可以通过二进制的位运算找到我们要修改的节点。其原理是利用了负数的补码表示,补码是原码取反加一。我们通过这个函数来定位我们将要修改的值。


update(x,d):


执行修改元素的功能,修改元素tree[x]=tree[x]+1,实际上在修改数组tree[],通过lowbit()找到下一个需要改变的树结点。直到x跳出最大值。


sum(x)


求前缀和a[1]+a[2]+a[3]+...+a[x]


discretize(lst):


离散化列表,把原来的数字,用他们的相对大小来替换原来的数值,而他们的顺序仍然不变,不影响逆序对的计算。


具体过程


我们先将树状数组初始化为0,再将输入的序列离散化,例如:

1 1234 23 234 3434 转化为1 4 2 3 5

再把数字看成树状数组的下标。每处理一个数字,树状数组的下标所对应的元素就+1,从后往前总计前缀和就是逆序对的数量,要参考上面的图。

我们从后往前遍历序列的时候,发现当前数x的前缀和sum(lists[i]-1)为几,则说明此时当前数的右边有几个比它小的数,我们就要在最终答案中加上sum(lists[i]-1),同时维护树状数组,updata(x,1),这里d我们只取1,意思是这个数被包含进了树状数组,在之后的查询中如果当前的序列中的数大于之前找到的数,就说明找到一对逆序对。


代码2


感谢蓝桥杯官网浪川博主大佬的主页

感谢蓝桥杯罗老师的教程

1. N=50010
2. tree=[0] * 50010
3. 
4. def lowbit(x):
5. return x&-x
6. 
7. def update(x,d):
8.     while(x<=N):
9.         tree[x]+=d
10.         x+=lowbit(x)
11. 
12. def sum(x):
13.     ans=0
14.     while x>0:
15.         ans+=tree[x]
16.         x-=lowbit(x)
17. return ans
18. 
19. 
20. def discretize(lst):
21.     new_lists=sorted(lst)
22.     a=[0]
23. for i in range(len(lst)):
24. for j in range(1,len(lst)+1):
25. if new_lists[j-1] == lst[i] and j not in a:
26.           a.append(j)
27. return a
28. 
29. n=int(input())
30. lists=discretize(list(map(int,input().split())))
31. 
32. print(lists)
33. 
34. num=0
35. for i in range(len(lists)-1,0,-1):
36.   update(lists[i],1)
37.   num+=sum(lists[i]-1)
38. 
39. print(num)
目录
相关文章
|
存储 程序员 索引
python字典排序、列表排序、升序、降序、逆序如何区别使用?
python字典排序、列表排序、升序、降序、逆序如何区别使用?
257 0
|
存储 程序员 索引
python中序列的排序,包括字典排序、列表排序、升序、降序、逆序
python中序列的排序,包括字典排序、列表排序、升序、降序、逆序
146 0
|
Python
Python经典编程习题100例:第40例:数组逆序输出
Python经典编程习题100例:第40例:数组逆序输出
80 0
|
算法 索引 Python
Python每日一练(牛客网新题库)——第2天:逆序输出字符串
Python每日一练(牛客网新题库)——第2天:逆序输出字符串
275 1
|
Python
ZZULIOJ-1060,逆序数字(Python)
ZZULIOJ-1060,逆序数字(Python)
|
Python
Python 使用列表的sort()进行多级排序实例演示,list的sort()排序方法使用详解,python3中sort()的cmp自定义排序方法,sort()的逆序、倒叙排序方法
Python 使用列表的sort()进行多级排序实例演示,list的sort()排序方法使用详解,python3中sort()的cmp自定义排序方法,sort()的逆序、倒叙排序方法
760 0
Python 使用列表的sort()进行多级排序实例演示,list的sort()排序方法使用详解,python3中sort()的cmp自定义排序方法,sort()的逆序、倒叙排序方法
|
4天前
|
数据挖掘 索引 Python
Python数据挖掘编程基础3
字典在数学上是一个映射,类似列表但使用自定义键而非数字索引,键在整个字典中必须唯一。可以通过直接赋值、`dict`函数或`dict.fromkeys`创建字典,并通过键访问元素。集合是一种不重复且无序的数据结构,可通过花括号或`set`函数创建,支持并集、交集、差集和对称差集等运算。
14 9
|
4天前
|
存储 开发者 Python
探索Python编程的奥秘
【9月更文挑战第29天】本文将带你走进Python的世界,通过深入浅出的方式,解析Python编程的基本概念和核心特性。我们将一起探讨变量、数据类型、控制结构、函数等基础知识,并通过实际代码示例,让你更好地理解和掌握Python编程。无论你是编程新手,还是有一定基础的开发者,都能在这篇文章中找到新的启示和收获。让我们一起探索Python编程的奥秘,开启编程之旅吧!
下一篇
无影云桌面