分治策略解决“最大子数组”问题

       分治策略,大致可以理解为,我们遇到一个规模比较大的问题,可以考虑将其划分为若干个规模较小的问题,然后规模较小的问题可以再继续分,知道问题变得显然容易解决。听起来和递归有点相似,严格上讲,分治是一种思想,递归是对自身的调用,二者是不同层次的概念,但是他们都是在降低需要处理的规模。

       最大子数组问题,给出一个数组,其中有实数若干,我们要找到一个子数组(连续的),使得该子数组的元素和是最大的。设我们的数组长度为 n,那么用暴力计算的方法,对于前 n - 1 个数中的第 i 个(i >= 1),有 n - i 种情况,一共有 \frac{n(n -1))}{2} 种情况,也就是 \Theta (n^{2}) 的时间复杂度。但是使用分治策略的话我们可以取得更优的时间复杂度。

       我们在这个数组中找到一个中点 mid,把数组分成两部分。那么如果该数组存在一个 "最大子数组",那么该 "最大子数组" 的位置可能有三种情况:第一种就是完全在 mid 的左边,第二种就是完全在 mid 的右边,第三种就是跨越 mid。那么该数组的 "最大子数组" 是 mid 左边的子数组、mid 右边的子数组、跨越 mid 的 "最大子数组" 这三个中的最大者。我们先来看当确定了会跨越 mid 时,这种情形下的 "最大子数组" 是啥样的:很显然把从 mid 开始,即以 mid 为起点,往两个方向延伸的最大数组组合起来即可。因为是有一端固定所以很简单,时间复杂度为 \Theta \left ( n \right )。而 mid 左边的子数组、mid 右边的子数组,调用自身这个函数来处理,这里的是递归的调用了。代码如下:

def LongestMaxSubstring(s):
	
	l = len(s)
	if l == 1:
		return 0, 0, s[0]
	elif l == 2:
		if s[0] > 0 and s[1] > 0:
			return 0, 1, s[0] + s[1]
		elif s[0] > s[1]:
			return 0, 0, s[0]
		elif s[0] <= s[1]:
			return 1, 1, s[1]
	else:
		mid = int(l/2)
		left_low, left_high, left_sum = LongestMaxSubstring(s[:mid])
		right_low, right_high, right_sum = LongestMaxSubstring(s[:mid])
		cross_low, cross_high, cross_sum = LongestMaxCrossingSubstring(s, mid)
		if left_sum >= max(right_sum, cross_sum):
			return left_low, left_high, left_sum
		elif right_sum >= max(left_sum, cross_sum):
			return right_low, right_high, right_sum
		else:
			return cross_low, cross_high, cross_sum
		
def LongestMaxCrossingSubstring(s, mid):
	left_sum = None
	left_position = mid
	right_sum = None
	right_position = mid
	for i in range(mid, 0 - 1, -1):
		if left_sum:
			if left_sum < sum(s[i: mid + 1]):
				left_sum = sum(s[i: mid + 1])
				left_position = i
		else:
			left_sum = s[mid]
	for j in range(mid, len(s)):
		if right_sum:
			if right_sum < sum(s[mid: j + 1]):
				right_sum = sum(s[mid:j + 1])
				right_position = j
		else:
			right_sum = s[mid]
	return left_position, right_position, right_sum + left_sum - s[mid]

        可能有的人会感到疑惑,因为似乎并没有解决问题,关于如果找到 “最大子数组”,我们依然没有一个公式一样的东西可以直接套进去进行计算。但是实际上,大多数问题都是无法公式化的处理,这种把问题进行转化的思维才是解决问题的关键。不过必须提到一点,这种算法代码量大,在解决小规模问题时的耗时很可能会大于暴力破解,他的优点在于大规模的情况。我用一个示意图来展示算法运行的过程,如下。

分治策略解决“最大子数组”问题_第1张图片

    

你可能感兴趣的:(python)