给定序列,找到最大的“反向排序”子序列

时间:2013-01-30 07:48:38

标签: c# algorithm linq

(是的,我尽可能多地搜索...) 也许我不应该使用序列,但没有更好的方法来描述它..

[修订:如答案部分所述,子序列不是正确的描述。 一对数字是正确的词!]

给定一系列数字,并将序列中一对数字的大小定义为两个数字之间的距离。显然最大的一对是[第一个数字,最后一个数字]。问题是找到与[first,last]对相反的最大数字对。

例如,如果序列是{1,6,3,5,2,8},那么答案应该是。{ [6,2]因为[1,8]的顺序正在增加,而且具有递减顺序的最大对是[6,2]。

一个附带问题是,这可以使用SQL语句以声明方式解决吗?具体来说,我正在考虑使用LINQ来做到这一点。

感谢。

3 个答案:

答案 0 :(得分:0)

这不是适合SQL的问题。它的措辞也很差。你真的在寻找数字对而不是子序列,大小就是两者之间的距离。

通过向后扫描序列,在给定点之后的最小数字及其位置,可以解决 O(n)。这会得到{1,2,2,2,2,8){0,4,4,4,4,5}。然后与原始序列并行扫描,得到{0,3,2,1,0,0}的大小,因此最大的大小是(6,2)对,其大小为3.

答案 1 :(得分:0)

未经测试,但我会尝试以下方法:

var sequence = new[] {1, 6, 3, 5, 2, 8};
var isGoingUp = sequence.First() < sequence.Last();

//if the first sequence is going up, invert the sign on the sequence
//this way we can always compare using "<"
var sequenceToCheck = sequence.Select(x => isGoingUp ? -x : x).ToList();

//initial values
int currentLength = 1; //the length of the subsequence found
int startIndex = 0; //the starting index of the subsequence

//check for each item's longest subsequence
for (int i = 0; i < sequenceToCheck.Count; i++)
{
    //go from the end towards the beginning and find the longest sequence for i
    for (int j = sequenceToCheck.Count - 1; i > j; i--)
    {
        if (sequenceToCheck[i] < sequenceToCheck[j])
        {
            //found i's longest subsequence
            if (currentLength < j - i + 1)
            {
                startIndex = i;
                currentLength = j - i + 1;
            }
            break; //don't check any smaller sequences for i
        }
    }

    if(sequenceToCheck.Count - i < currentLength)
        break; //no need to check any more, no sequence past this can be longer
}

var subsequence = sequence.Skip(startIndex).Take(currentLength);

您应该能够在LINQ中实现此解决方案,方法是计算从每个点到匹配数字的距离(从结尾开始)。


然而,在SQL中实现它会稍微困难一点,尽管你绝对可以做到这一点。这是我的尝试和working SQL Fiddle example

WITH firstValue AS (
    SELECT TOP 1 value
    FROM t
    ORDER BY id
), lastValue AS (
    SELECT TOP 1 value
    FROM t
    ORDER BY id DESC
), seqToCheck AS (
    SELECT id, 
        CASE WHEN lastValue.value > firstValue.value THEN 0-t.value 
             ELSE t.value 
        END AS value
    FROM t
    CROSS JOIN firstValue
    CROSS JOIN lastValue
), subSequences AS (
    SELECT s1.id, COALESCE(MAX(s2.id - s1.id + 1),1) AS distance
    FROM seqToCheck AS s1
    LEFT JOIN seqToCheck AS s2 ON s1.value < s2.value AND s2.id > s1.id
    GROUP BY s1.id
), longestSubSequence AS (
    SELECT TOP 1 id, distance
    FROM subSequences
    ORDER BY distance DESC
)
SELECT t.value
FROM t
INNER JOIN longestSubSequence AS l ON t.id >= l.id AND t.id < l.id + l.distance
ORDER BY t.id

这假设您有一个升序的标识列id 不包含空白。如果你没有这样的话,你也可以ROW_NUMBER() OVER(ORDER BY xxxxx)

为了使用索引,你可能会使它更复杂一些。

答案 2 :(得分:0)

一个好的算法是从外部进行比较,一旦得到你需要的东西,就会中断。因为你的目标序列需要在第一个和最后一个之间进行比较,所以这个问题很容易实现。从外部缩小序列可确保您更快地获得所需的最大序列。

在你的情况下, 的 {1,6,3,5,2,8}

1. Compare 1 and 8 
2. Compare 1 and 2
3. Compare 6 and 8
4. Compare 6 and 2 and you got your sequence