小和问题:
排序大家都不陌生吧,想必每个从事计算机方向的人,或多或少知道一些排序的方法吧如冒泡,插入,选择,快排,希尔,归并,堆排等排序方法:那本次要讲的小和问题实际上就是归并排序的一个衍生,其思想就类似与归并排序。
归并排序就是将一串数组通过二分的方法分割直到元素不可分为止,然后在进行合并,合并中就可以小范围的进行每一组的排序,从而达到排序的结果。
那么小和问题的思想就是类似于归并排序。
那什么是小和问题呢?
给定一个数组,当数组中左边的元素小于右边的元素是就讲左边的元素累加起来构成这个数组的小和
例:[1,2,5,4]的小和为:1+1+2+1+2 = 7
当你看大这道题目是可能会说这道题并不难可以用两个for循环就可以解决。
第一个for循环是用来遍历整个数组,第二个for循环嵌套在第一个for循环之间 用来求出当前位置的小和然后通过循环和累加的形式就可以求出结果没有一点难度。
很显然这种方法是对的但是!!
引用剑指offer中的一句话:“很显然这不足以拿到offer” 是不是感觉自己绿了
因为此方法的时间复杂度为 O(n^2)
回到之前所说的用归并排序的方法解决小和问题(我尽我最大的努力讲清楚)
首先通过递归的方法对此数组进行拆分
当拆分到最后一层时每一个组只有一个元素,所以他们的小和为0
然后合并如果是两个时如果满足小和的要求就累加,同时对已经求出小和的部分进行排序(与归并排序相同)
然后在和上一层进行比较(此时上一组也进项了排序)重点来了
如 [1,2] [3,4]
此时组1的和组2都是经过排序后的所以只需要组与组之间进行比较
拿下标0与下标2进行比较如果2比0大的话就代表2后面的数字都比0大 所以直接可以把2后面的全部和0比较所以这两组中中就有2后面所有的元素个数+1 :
然后1中的下一个元素在与下标2进行比较:
如果满足条件类似于上次操作
如果不满足则组2的下标移下一位和不变
最后返回到递归最后一层我们需要把左边部分的和和右边部分的和再加上整个元素总体的和就为小和
代码如下:
#coding=utf8
class Small_Sum():
def small_sums(self,li):
"""当为空或者长度小于2是返回0"""
if li is None or len(li)<2:
return 0
return self.small_sum(li,0,len(li)-1)
#进行递归的函数
def small_sum(self,li,l,r):
if l == r:
return 0
mid = l + ((r-l) // 2)
# l = self.small_sum(li,l,mid)
# r = self.small_sum(li,mid+1,r)
return self.small_sum(li,l,mid) + self.small_sum(li,mid+1,r) + self.merge(li,l,mid,r)
#找出小和与排序
def merge(self,li,l,mid,r):
res = 0
help = [] #定义临时列表来暂时存储元素
p1 = l
p2 = mid+1
while p1 <= mid and p2 <= r:
res += (r - p2 + 1) * li[p1] if li[p1] < li[p2] else 0
if li[p1] < li[p2]:
help.append(li[p1])
p1+=1
else:
help.append(li[p2])
p2+=1
#两个循环只能进去一个
while p1 <=mid:
help.append(li[p1])
p1 +=1
while p2 <= r:
help.append(li[p2])
p2 +=1
li[l:r+1] = help
return res #返回小和
if __name__ == '__main__':
li = [1,2,5,4]
ss = Small_Sum()
sum = ss.small_sums(li)
print(sum)