Codility每周一课:P99.4 ArrayInversio

P99.4 ArrayInversionCount
Compute number of inversion in an array.
-
P99.4 逆序索引对
计算数组中的逆序索引对的数量
给出一个由N个整数组成的数组A。索引对(P,Q)是逆序的,如果P<Q,并且A[Q] < A[P]。
编写函数:
def solution(A)
计算A中的逆序索引对的数量,如果它超过1,000,000,000,则返回−1。
例如,对于数组A:A[0]=-1,A[1]=6,A[2]=3,A[3]=4,A[4]=7,A[5]=4。有4个逆序索引对:(1,2)(1,3)(1,5)(4,5),函数应该返回4。
假定:
- N是区间[0,100,000]内的整数;
- 数组A的每个元素都是区间-2,147,483,648,2,147,483,647]内的整数;
- 解题思路
直接计算的时间复杂度为O(N**2),显然不可取。数组是无序的,因此考虑利用排序算法解决此问题,也就是在排序过程中计算逆序索引对的个数。现在说明利用归并排序解决此问题。
归并排序就是将数组A分左、右两部分。最终的逆序索引对的个数就等于左部分的逆序索引对的个数、右部分的逆序索引对的个数与合并左右2部分得到的逆序索引对之和。但归根结底这个逆序索引对的个数都是通过合并已经排好序的左右2部分得到的。
下面给出合并已经排好序的左右两部分的逆序索引对的计算过程:
假设A=[3, 11, 8, 10, 3],开始分为2部分AL= [3, 11, 8] 和 AR= [10, 3],排序后分别是SL=[3, 8, 11] 和SR= [3, 10]。最终合并后的序列为K。
-
SL[0]=3和SR[0]=3。前者不大于后者,不存在逆序索引对;SL删除索引为0的元素。SL=[8, 11] ,K添加被删除的元素K=[3];
-
SL[0]=8和SR[0]=3。前者大于,因此存在逆序索引对,并且8之后的元素都是逆序索引对,也就是有2个。SR删除索引为0的元素。SR=[10],K添加被删除的元素K=[3, 3];
-
SL[0]=8和SR[0]=10。前者不大于,因此不存在逆序索引对,SL删除索引为0的元素。SL=[11] ,K添加被删除的元素K=[3, 3, 8];
-
SL[0]=11和SR[0]=10。前者大于,因此存在逆序索引对,因为SL只有这一个元素了,因此逆序索引数为1个。SR删除索引为0的元素。SL=[] ,K添加被删除的元素K=[3, 3, 8, 10];
-
左右有一个为空就停止判断。最后K添加SL剩余的元素K=[3, 3, 8, 10, 11],结束;
也就是说从[3, 8, 11, 3, 10]变为[3, 3, 8, 10, 11]一共有3个逆序索引数对。 同理可得到[3, 11, 8]变为[3, 8, 11]有1个, [10, 3]变为[3, 10]也有1个。因此一共5个。
- Python3代码
# -*- coding:utf-8 -*-
# &Author AnFany
# Lesson 99:Future training
# P 99.4 ArrayInversionCount
def solution_direct(A):
"""
返回数组A中逆序索引对的个数
时间复杂度O(N**2)
:param A: 数组
:return: 逆序索引对的个数
"""
return len([0 for i in range(len(A)) for j in range(i, len(A)) if A[i] > A[j]])
def merge(a):
"""
实现归并排序
:param a:数组
:return:逆序索引对的个数
"""
# 总的逆序索引对的个数
length = len(a)
if length <= 1:
return a, 0
# 分割点
middle = length // 2
# 左边
left_list, left_count = merge(a[: middle])
# 右边
right_list, right_count = merge(a[middle:])
# 左右之和
count = left_count + right_count
# 合并在一起的序列,也就是排序后的系列
sorted_list = []
# 合并时候的逆序数
while len(left_list) and len(right_list):
if left_list[0] > right_list[0]:
len_left = len(left_list)
count += len_left
small = right_list.pop(0)
sorted_list.append(small)
else:
small = left_list.pop(0)
sorted_list.append(small)
if left_list:
sorted_list += left_list
else:
sorted_list += right_list
return sorted_list, count
def solution(A):
"""
返回数组A中逆序索引对的个数
利用归并排序的思想,最终的逆序数对的个数=左序列排完序得到的逆序数+右序列排完序得到的逆序数+合并左右得到的逆序数
其实所有的逆序数归根结底都是合并左右得来的逆序数
时间复杂度:O(N*log(N))
:param A: 数组
:return: 逆序索引对的个数
"""
length = len(A)
if length <= 1:
return 0
count = merge(A)[1]
if count > 1e9:
return -1
return count
- 结果
点击获得更多编程练习题。欢迎Follow,感谢Star!!! 扫描关注微信公众号pythonfan,获取更多。