有一个二维数组,如 -
a[0] = [ 0 , 4 , 9 ] a[1] = [ 2 , 6 , 11 ] a[2] = [ 3 , 8 , 13 ] a[3] = [ 7 , 12 ]
需要从每个子数组中选择一个元素,使得结果数组最接近,即集合中最高数字和最低数字之间的差异最小。
以上答案为= [ 9 , 6 , 8 , 7 ]
。
已经制定了算法,但感觉不是很好。 在时间和空间复杂性方面,这样做的有效算法是什么?
编辑 - 我的算法(在python中) -
INPUT - Dictionary : table{} OUTPUT - Dictionary : low_table{} # N = len(table) for word_key in table: for init in table[word_key]: temp_table = copy.copy(table) del temp_table[word_key] per_init = copy.copy(init) low_table[init]=[] for ite in range(N-1): min_val = 9999 for i in temp_table: for nums in temp_table[i]: if min_val > abs(init-nums): min_val = abs(init-nums) del_num = i next_num = nums low_table[per_init].append(next_num) init = (init+next_num)/2 del temp_table[del_num] lowest_val = 99 lowest_set = [] for x in low_table: low_table[x].append(x) low_table[x].sort() mini = low_table[x][-1]-low_table[x][0] if mini < lowest_val: lowest_val = mini lowest_set = low_table[x] print lowest_set
答案 0 :(得分:11)
收集所有值以创建单个有序序列,每个元素都标记有来自的数组: 0(0),2(1),3(2),4(0),6(1),... 12(3),13(2)
然后在它们之间创建一个窗口,从第一个开始(0(0))并在第一个位置结束,使窗口跨越所有数组(0(0) - > 7(3))
然后通过将窗口的开头递增1来滚动此窗口,并递增窗口的末尾,直到再次有一个覆盖所有元素的窗口。
然后再次滚动:(2(1),3(2),4(0),... 7(3)),依此类推。
在每一步跟踪最大和最小之间的差异。最终你会发现窗口最小的那个。我觉得在最坏的情况下这是O(n ^ 2),但这只是猜测。
答案 1 :(得分:2)
whiterook6的好算法的一个罗嗦的Haskell版本:
import Data.List (minimumBy,sortBy)
import qualified Data.Map as M (fromList,toList,adjust,lookup)
f arrays = g (zip arrays [1..]) [] h [(100,0),(0,0)] where
n = length arrays
h = (M.fromList $ zip [1..n] (repeat 0))
g arrays sequence indexes best
| any ((==0) . snd) (M.toList indexes) =
g (foldr comb [] arrays) (next:sequence) (M.adjust (+1) ind indexes) best
| otherwise =
if null (drop 1 arrays)
then best'
else g (foldr comb [] arrays)
(next:init trimmedSequence)
(foldr (M.adjust (+1)) h (ind : (map snd $ init trimmedSequence)))
best'
where
best' = minimumBy comp [best,trimmedSequence]
next@(val,ind) = minimum $ map (\(arr,i) -> (head arr,i)) arrays
comb a@(seq,i) b = if i == ind
then if null (drop 1 seq)
then b
else (drop 1 seq,i) : b
else a : b
comp a b = compare (fst (head a) - fst (last a)) (fst (head b) - fst (last b))
trimSequence [] _ = []
trimSequence (x:xs) h
| any ((==0) . snd) (M.toList h) =
case M.lookup (snd x) h of
Just 0 -> x : trimSequence xs (M.adjust (+1) (snd x) h)
otherwise -> trimSequence xs h
| otherwise = []
trimmedSequence = trimSequence sequence (M.fromList $ zip [1..n] (repeat 0))
输出:
*Main> f [[0,4,9],[2,6,11],[3,8,13],[7,12]]
[(9,1),(8,3),(7,4),(6,2)]
答案 2 :(得分:1)
我有一个whiterook算法的变体我认为更简单(除了初始排序步骤,它更明显是O(N))。
按顺序迭代所有值的minval。在执行此操作时,我们将每个数组中最小值的索引保持为大于或等于minval。我们还将这些索引处元素的最大值保持在相应的数组中。
当我们考虑了特定的minval时,我们可以增加包含minval的所有数组的索引,并在必要时更新maxval。
这是一个实现。注意(初始排序除外)它是O(N),因为每个数组中每个值最多执行一次内循环。
def minspan(aa):
allnums = sorted(set(sum(aa, [])))
ntoi = dict((x, []) for x in allnums)
for i in xrange(len(aa)):
for x in aa[i]:
ntoi[x].append(i)
indexes = [0] * len(aa)
maxval = max(a[0] for a in aa)
best = None
for minval in allnums:
if best is None or best[0] > maxval - minval:
best = maxval - minval, minval, maxval
for i in ntoi[minval]:
indexes[i] += 1
if indexes[i] >= len(aa[i]):
return [min(x for x in a if x >= best[1]) for a in aa]
maxval = max(maxval, aa[i][indexes[i]])
aa = [[0,4,9], [2,6,11], [3,8,13], [7,12]]
print minspan(aa)