算法:
如果存在差异,则将一系列数字称为摆动序列 在连续数字之间严格地在正数和 负。第一个差异(如果存在)可能是积极的 或否定的。具有少于两个元素的序列通常是a 摆动序列。
例如,[1,7,4,9,2,5]是一个摆动序列,因为 差异(6,-3,5,-7,3)交替为正和负。在 相比之下,[1,4,7,2,5]和[1,7,4,5,5]不是摆动序列, 首先是因为它的前两个差异是积极的,第二个是差异 因为它的最后一个差异是零。
给定一系列整数,返回最长的长度 子序列是一个摆动序列。通过以下方式获得子序列 从中删除一些元素(最终也是零) 原始序列,将剩余的元素留在原始元素中 顺序。
示例:
_JAVA_OPTIONS
我的解决方案:
Input: [1,7,4,9,2,5]
Output: 6
The entire sequence is a wiggle sequence.
Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].
Input: [1,2,3,4,5,6,7,8,9]
Output: 2
这适用于较小的输入(例如[1,1,1,3,2,4,1,6,3,10,8]和所有样本输入,但是对于非常大的输入(其中更难调试),如:
[33,53,12,64,50,41,45,21,97,35,47,92,39,0,93,55,40,46,69,42,6,95,51, 68,72,9,32,84,34,64,6,2,26,98,3,43,30,60,3,68,82,9,97,19,27,98,99,4, 30,96,37,9,78,43,64,4,65,30,84,90,87,64,18,50,60,1,40,32,48,50,76,100,57,29, 63,53,46,57,93,98,42,80,82,9,41,55,69,84,82,79,30,79,18,97,67,23,52,38,74, 15]
应该有输出:67但是我的soln输出57.有谁知道这里有什么问题?
答案 0 :(得分:3)
尝试的方法是一个贪婪的解决方案(因为它总是使用当前元素,如果它满足摆动条件),但这并不总是有效。
我将尝试使用这个更简单的反例来说明这一点:1 100 99 6 7 4 5 2 3
。
一个最佳子序列是:1 100 6 7 4 5 2 3
,但算法中的两个build_seq
调用将生成以下序列:
1 100 99
1
编辑:稍加修改的贪婪方法确实有效 - 请参阅this link,谢谢Peter de Rivaz。
答案 1 :(得分:1)
动态编程可用于获得最佳解决方案。
注意:我在看到@PeterdeRivaz提到的article之前写了这个。虽然动态编程(O(n 2 ))有效,但本文提出了一种优越的(O(n))“贪婪”算法(“方法#5”),它比编码更容易。动态编程解决方案。我添加了第二个实现该方法的答案。
<强>代码强>
def longest_wiggle(arr)
best = [{ pos_diff: { length: 0, prev_ndx: nil },
neg_diff: { length: 0, prev_ndx: nil } }]
(1..arr.size-1).each do |i|
calc_best(arr, i, :pos_diff, best)
calc_best(arr, i, :neg_diff, best)
end
unpack_best(best)
end
def calc_best(arr, i, diff, best)
curr = arr[i]
prev_indices = (0..i-1).select { |j|
(diff==:pos_diff) ? (arr[j] < curr) : (arr[j] > curr) }
best[i] = {} if best.size == i
best[i][diff] =
if prev_indices.empty?
{ length: 0, prev_ndx: nil }
else
prev_diff = previous_diff(diff)
j = prev_indices.max_by { |j| best[j][prev_diff][:length] }
{ length: (1 + best[j][prev_diff][:length]), prev_ndx: j }
end
end
def previous_diff(diff)
diff==:pos_diff ? :neg_diff : :pos_diff·
end
def unpack_best(best)
last_idx, last_diff =
best.size.times.to_a.product([:pos_diff, :neg_diff]).
max_by { |i,diff| best[i][diff][:length] }
return [0, []] if best[last_idx][last_diff][:length].zero?
best_path = []
loop do
best_path.unshift(last_idx)
prev_index = best[last_idx][last_diff][:prev_ndx]
break if prev_index.nil?
last_idx = prev_index·
last_diff = previous_diff(last_diff)
end
best_path
end
<强>实施例强>
longest_wiggle([1, 4, 2, 6, 8, 3, 2, 5])
#=> [0, 1, 2, 3, 5, 7]]
最长摆动的长度为6,由指数0
,1
,2
,3
,5
和{{ 1}},即7
。
第二个例子使用问题中给出的较大数组。
[1, 4, 2, 6, 3, 5]
如上所述,最大的摆动由arr = [33, 53, 12, 64, 50, 41, 45, 21, 97, 35, 47, 92, 39, 0, 93, 55, 40, 46,
69, 42, 6, 95, 51, 68, 72, 9, 32, 84, 34, 64, 6, 2, 26, 98, 3, 43, 30,
60, 3, 68, 82, 9, 97, 19, 27, 98, 99, 4, 30, 96, 37, 9, 78, 43, 64, 4,
65, 30, 84, 90, 87, 64, 18, 50, 60, 1, 40, 32, 48, 50, 76, 100, 57, 29,
arr.size 63, 53, 46, 57, 93, 98, 42, 80, 82, 9, 41, 55, 69, 84, 82, 79, 30, 79,
18, 97, 67, 23, 52, 38, 74, 15]
#=> 100
longest_wiggle(arr).size
#=> 67
longest_wiggle(arr)
#=> [0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14, 16, 17, 19, 21, 22, 23, 25,
# 27, 28, 29, 30, 32, 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 47, 49, 50,
# 52, 53, 54, 55, 56, 57, 58, 62, 63, 65, 66, 67, 70, 72, 74, 75, 77, 80,
# 81, 83, 84, 90, 91, 92, 93, 95, 96, 97, 98, 99]
的67个元素组成。解决方案时间基本上是即时的。
这些指数的arr
值如下。
arr
<强>解释强>
我本来打算提供一个算法及其实现的解释,但从那时起我就学到了一种优越的方法(参见我在答案开头的注释),我决定不这样做,但当然是很乐意回答任何问题。我的笔记中的链接解释了如何在这里使用动态编程。
答案 2 :(得分:0)
设Wp [i]是从元素i开始的最长的摆动序列,其中第一个差异是正的。设Wn [i]是相同的,但第一个差异是负的。
然后:
Wp[k] = max(1+Wn[k'] for k<k'<n, where A[k'] > A[k]) (or 1 if no such k' exists)
Wn[k] = max(1+Wp[k'] for k<k'<n, where A[k'] < A[k]) (or 1 if no such k' exists)
这给出了一个O(n ^ 2)动态编程解决方案,这里是伪代码
Wp = [1, 1, ..., 1] -- length n
Wn = [1, 1, ..., 1] -- length n
for k = n-1, n-2, ..., 0
for k' = k+1, k+2, ..., n-1
if A[k'] > A[k]
Wp[k] = max(Wp[k], Wn[k']+1)
else if A[k'] < A[k]
Wn[k] = max(Wn[k], Wp[k']+1)
result = max(max(Wp[i], Wn[i]) for i = 0, 1, ..., n-1)
答案 3 :(得分:0)
在对@quertyman的回答的评论中,@ PeterdeRivaz提供了article的链接,该链接考虑了解决“最长的摆动子序列”问题的各种方法。我实施了“方法#5”,其时间复杂度为O(n)。
算法既简单又快速。第一步是从每对相等的连续元素中删除一个元素,并继续这样做直到没有相等的连续元素。例如,[1,2,2,2,3,4,4]
将转换为[1,2,3,4]
。最长的摆动子序列包括结果数组的第一个和最后一个元素a
,以及a[i]
或0 < i < a.size-1
的每个元素a[i-1] < a[i] > a[i+1]
,a[i-1] > a[i] > a[i+1]
。换句话说,它包括第一个和最后一个元素以及所有峰和谷底。这些元素在下图中是A,D,E,G,H,I(取自上述文章,经许可)。
<强>代码强>
def longest_wiggle(arr)
arr.each_cons(2).
reject { |a,b| a==b }.
map(&:first).
push(arr.last).
each_cons(3).
select { |triple| [triple.min, triple.max].include? triple[1] }.
map { |_,n,_| n }.
unshift(arr.first).
push(arr.last)
end
示例强>
arr = [33, 53, 12, 64, 50, 41, 45, 21, 97, 35, 47, 92, 39, 0, 93, 55, 40,
46, 69, 42, 6, 95, 51, 68, 72, 9, 32, 84, 34, 64, 6, 2, 26, 98, 3,
43, 30, 60, 3, 68, 82, 9, 97, 19, 27, 98, 99, 4, 30, 96, 37, 9, 78,
43, 64, 4, 65, 30, 84, 90, 87, 64, 18, 50, 60, 1, 40, 32, 48, 50, 76,
100, 57, 29, 63, 53, 46, 57, 93, 98, 42, 80, 82, 9, 41, 55, 69, 84,
82, 79, 30, 79, 18, 97, 67, 23, 52, 38, 74, 15]
a = longest_wiggle(arr)
#=> [33, 53, 12, 64, 41, 45, 21, 97, 35, 92, 0, 93, 40, 69, 6, 95, 51, 72,
# 9, 84, 34, 64, 2, 98, 3, 43, 30, 60, 3, 82, 9, 97, 19, 99, 4, 96, 9,
# 78, 43, 64, 4, 65, 30, 90, 18, 60, 1, 40, 32, 100, 29, 63, 46, 98, 42,
# 82, 9, 84, 30, 79, 18, 97, 23, 52, 38, 74, 15]
a.size
#=> 67
<强>解释强>
步骤如下。
arr = [3, 4, 4, 5, 2, 3, 7, 4]
enum1 = arr.each_cons(2)
#=> #<Enumerator: [3, 4, 4, 5, 2, 3, 7, 4]:each_cons(2)>
我们可以看到这个枚举器通过将它转换为数组而生成的元素。
enum1.to_a
#=> [[3, 4], [4, 4], [4, 5], [5, 2], [2, 3], [3, 7], [7, 4]]
继续,删除每组连续相等元素中的一个。
d = enum1.reject { |a,b| a==b }
#=> [[3, 4], [4, 5], [5, 2], [2, 3], [3, 7], [7, 4]]
e = d.map(&:first)
#=> [3, 4, 5, 2, 3, 7]
添加最后一个元素。
f = e.push(arr.last)
#=> [3, 4, 5, 2, 3, 7, 4]
接下来,找到山峰和谷底。
enum2 = f.each_cons(3)
#=> #<Enumerator: [3, 4, 5, 2, 3, 7, 4]:each_cons(3)>
enum2.to_a
#=> [[3, 4, 5], [4, 5, 2], [5, 2, 3], [2, 3, 7], [3, 7, 4]]
g = enum2.select { |triple| [triple.min, triple.max].include? triple[1] }
#=> [[4, 5, 2], [5, 2, 3], [3, 7, 4]]
h = g.map { |_,n,_| n }
#=> [5, 2, 7]
最后,添加arr
的第一个和最后一个值。
i = h.unshift(arr.first)
#=> [3, 5, 2, 7]
i.push(arr.last)
#=> [3, 5, 2, 7, 4]