这是一个典型的面试问题。给定一个包含正负元素而不包含0的数组,找到最大的子数组,其总和等于0.我试图解决这个问题。这就是我提出的。
def sub_array_sum(array,k=0):
start_index = -1
hash_sum = {}
current_sum = 0
keys = set()
best_index_hash = {}
for i in array:
start_index += 1
current_sum += i
if current_sum in hash_sum:
hash_sum[current_sum].append(start_index)
keys.add(current_sum)
else:
if current_sum == 0:
best_index_hash[start_index] = [(0,start_index)]
else:
hash_sum[current_sum] = [start_index]
if keys:
for k_1 in keys:
best_start = hash_sum.get(k_1)[0]
best_end_list = hash_sum.get(k_1)[1:]
for best_end in best_end_list:
if abs(best_start-best_end) in best_index_hash:
best_index_hash[abs(best_start-best_end)].append((best_start+1,best_end))
else:
best_index_hash[abs(best_start-best_end)] = [(best_start+1,best_end)]
if best_index_hash:
(bs,be) = best_index_hash[max(best_index_hash.keys(),key=int)].pop()
return array[bs:be+1]
else:
print "No sub array with sum equal to 0"
def Main():
a = [6,-2,8,5,4,-9,8,-2,1,2]
b = [-8,8]
c = [-7,8,-1]
d = [2200,300,-6,6,5,-9]
e = [-9,9,-6,-3]
print sub_array_sum(a)
print sub_array_sum(b)
print sub_array_sum(c)
print sub_array_sum(d)
print sub_array_sum(e)
if __name__ == '__main__':
Main()
我不确定这是否能满足所有边缘情况。如果有人可以对此发表评论,那就太棒了。我也希望将其扩展到等于任何K的总和,而不仅仅是0.我应该怎么做呢。任何进一步优化这一点的指针也很有帮助。
答案 0 :(得分:2)
这是我自己的答案,只是为了好玩。
子序列的数量是二次的,并且子序列求和的时间是线性的,所以最天真的解是立方。
这种方法只是对子序列的详尽搜索,但是一点点诡计避免了线性求和因子,所以它只是二次方。
from collections import namedtuple
from itertools import chain
class Element(namedtuple('Element', ('index', 'value'))):
"""
An element in the input sequence. ``index`` is the position
of the element, and ``value`` is the element itself.
"""
pass
class Node(namedtuple('Node', ('a', 'b', 'sum'))):
"""
A node in the search graph, which looks like this:
0 1 2 3
\ / \ / \ /
0-1 1-2 2-3
\ / \ /
0-2 1-3
\ /
0-3
``a`` is the start Element, ``b`` is the end Element, and
``sum`` is the sum of elements ``a`` through ``b``.
"""
@classmethod
def from_element(cls, e):
"""Construct a Node from a single Element."""
return Node(a=e, b=e, sum=e.value)
def __add__(self, other):
"""The combining operation depicted by the graph above."""
assert self.a.index == other.a.index - 1
assert self.b.index == other.b.index - 1
return Node(a=self.a, b=other.b, sum=(self.sum + other.b.value))
def __len__(self):
"""The number of elements represented by this node."""
return self.b.index - self.a.index + 1
def get_longest_k_sum_subsequence(ints, k):
"""The longest subsequence of ``ints`` that sums to ``k``."""
n = get_longest_node(n for n in generate_nodes(ints) if n.sum == k)
if n:
return ints[n.a.index:(n.b.index + 1)]
if k == 0:
return []
def get_longest_zero_sum_subsequence(ints):
"""The longest subsequence of ``ints`` that sums to zero."""
return get_longest_k_sum_subsequence(ints, k=0)
def generate_nodes(ints):
"""Generates all Nodes in the graph."""
nodes = [Node.from_element(Element(i, v)) for i, v in enumerate(ints)]
while len(nodes) > 0:
for n in nodes:
yield n
nodes = [x + y for x, y in zip(nodes, nodes[1:])]
def get_longest_node(nodes):
"""The longest Node in ``nodes``, or None if there are no Nodes."""
return max(chain([()], nodes), key=len) or None
if __name__ == '__main__':
def f(*ints):
return get_longest_zero_sum_subsequence(list(ints))
assert f() == []
assert f(1) == []
assert f(0) == [0]
assert f(0, 0) == [0, 0]
assert f(-1, 1) == [-1, 1]
assert f(-1, 2, 1) == []
assert f(1, -1, 1, -1) == [1, -1, 1, -1]
assert f(1, -1, 8) == [1, -1]
assert f(0, 1, -1, 8) == [0, 1, -1]
assert f(5, 6, -2, 1, 1, 7, -2, 2, 8) == [-2, 1, 1]
assert f(5, 6, -2, 2, 7, -2, 1, 1, 8) == [-2, 1, 1]
答案 1 :(得分:2)
你已经给出了一个很好的线性时间解决方案(比其他两个答案更好,这是二次时间),基于这样的想法,即每当sum(i ... j)= 0时,它必须是sum(0 .. i-1)= sum(0 .. j),反之亦然。基本上你计算所有i的前缀和sum(0 ... i),建立一个哈希表hash_sum
,其中hash_sum[x]
是所有位置的列表,我有总和(0 ... i)= x 。然后你经历这个哈希表,一次一个总和,寻找由多个前缀产生的任何总和。在所有这些多于一次的金额中,你选择的是由一对相距最远的前缀 - 这是最长的。
由于你已经注意到使这个算法成为线性时间所需的关键洞察力,我有点疑惑为什么你在第二个循环中在best_index_hash
中积累了这么多不必要的东西。对于给定的和x,构成该总和的最远的前缀对将始终是hash_sum[x]
中最小和最大的条目,这必然是第一个和最后一个条目(因为这是顺序它们被附加了),所以不需要在它们之间循环元素。事实上,您甚至根本不需要第二个循环:通过将start_index
视为最右端点,您可以在第一个循环期间保持运行最大值。
处理任意差异k:我们需要找到前缀最左边的前缀,而不是找到最后一个出现的前缀current_sum
。{{1} }。但那只是current_sum - k
。
以下代码未经过测试,但应该可以使用:
first_with_sum{current_sum - k}
在开始时设置def sub_array_sum(array,k=0):
start_index = -1
first_with_sum = {}
first_with_sum{0} = -1
best_start = -1
best_len = 0
current_sum = 0
for i in array:
start_index += 1
current_sum += i
if current_sum - k in first_with_sum:
if start_index - first_with_sum{current_sum - k} > best_len:
best_start = first_with_sum{current_sum - k} + 1
best_len = start_index - first_with_sum{current_sum - k}
else:
first_with_sum{current_sum} = start_index
if best_len > 0:
return array[best_start:best_start+best_len-1]
else:
print "No subarray found"
意味着我们不必将从索引0开始的范围视为特殊情况。请注意,此算法不会改进原始算法的渐近时间或空间复杂度,但实现起来更简单,并且在包含零和子数组的任何输入上将使用少量空间。
答案 2 :(得分:1)
我同意sundar nataraj,他说这必须发布到代码审查论坛。
虽然我看了你的代码,但很有趣。虽然我能够理解您的方法,但我无法理解使用Counter
的必要性。
best_index_hash[start_index] = [(0,start_index)]
- 此处best_index_hash
的类型为Counter
。你为什么要给它分配一个清单?
for key_1, value_1 in best_index_hash.most_common(1)
- 您尝试获取largest
子序列,并且您正在使用most_common
作为答案。这在语义上并不直观。
我很想发布解决方案,但我会等你编辑代码片段并改进它。
<强>附录强>
为了好玩,我抓住了这个难题,并在下面展示我的努力。我不保证正确/完整。
from collections import defaultdict
def max_sub_array_sum(a, s):
if a:
span = defaultdict(lambda : (0,0))
current_total = 0
for i in xrange(len(a)):
current_total = a[i]
for j in xrange (i + 1, len(a)):
current_total += a[j]
x,y = span[current_total]
if j - i > y - x:
span[current_total] = i,j
if s in span:
i, j = span[s]
print "sum=%d,span_length=%d,indices=(%d,%d),sequence=%s" %\
(s, j-i + 1, i, j, str(a[i:j + 1]))
return
print "Could not find a subsequence of sum %d in sequence %s" % \
(s, str(a))
max_sub_array_sum(range(-6, -1), 0)
max_sub_array_sum(None, 0)
max_sub_array_sum([], 0)
max_sub_array_sum(range(6), 15)
max_sub_array_sum(range(6), 14)
max_sub_array_sum(range(6), 13)
max_sub_array_sum(range(6), 0)
答案 3 :(得分:0)
这里是the solution taken from LeetCode:
extension ViewController: WKNavigationDelegate{
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
print(navigationAction.request.url ?? "")
if let url = navigationAction.request.url?.absoluteString{
}
decisionHandler(.allow)
}
}