O(nlgn)中最长的非递减子序列

时间:2014-02-12 00:04:14

标签: algorithm lis

我有以下算法,效果很好

我尝试在这里为自己http://nemo.la/?p=943解释它,并在此处解释http://www.geeksforgeeks.org/longest-monotonically-increasing-subsequence-size-n-log-n/以及stackoverflow以及

我想修改它以产生最长的非单调增加的子序列

序列30 20 20 10 10 10 10

答案应为4:“10 10 10 10”

但是使用nlgn版本的算法却无法正常工作。初始化s以包含第一个元素“30”并从第二个元素开始= 20.这就是:

  1. 第一步:30不大于或等于20.我们发现最小元素大于20.新s变为“20”

  2. 第二步:20大于或等于20.我们扩展序列,s现在包含“20 20”

  3. 第三步:10不大于或等于20.我们发现大于10的最小元素是“20”。新s变为“10 20”

  4. 并且在此之后s将永远不会增长,算法将返回2而不是4

    int height[100];
    int s[100];
    
    int binary_search(int first, int last, int x) {
    
        int mid;
    
        while (first < last) {
    
            mid = (first + last) / 2;
    
            if (height[s[mid]] == x)
                return mid;
    
            else if (height[s[mid]] >= x)
                last =  mid;
    
            else
                first = mid + 1;
        }
        return first; /* or last */
    }
    
    int longest_increasing_subsequence_nlgn(int n) {
    
        int i, k, index;
    
        memset(s, 0, sizeof(s));
    
        index = 1;
        s[1] = 0; /* s[i] = 0 is the index of the element that ends an increasing sequence of length  i = 1 */
    
        for (i = 1; i < n; i++) {
    
            if (height[i] >= height[s[index]]) { /* larger element, extend the sequence */
    
                index++; /* increase the length of my subsequence */
                s[index] = i; /* the current doll ends my subsequence */
    
            }
            /* else find the smallest element in s >= a[i], basically insert a[i] in s such that s stays sorted */
            else {
                k = binary_search(1, index, height[i]);
    
                if (height[s[k]] >= height[i]) { /* if truly >= greater */
                    s[k] = i;
                }
            }
        }
        return index;
    }
    

5 个答案:

答案 0 :(得分:3)

要找到最长的非严格增加的子序列,请更改以下条件:

  
      
  1. 如果A[i]在所有活动列表的最终候选者中最小,我们将启动新的有效列表1
  2.   
  3. 如果A[i]在所有活动列表的最终候选者中最大,我们将克隆最大的活动列表,并按A[i]进行扩展。
  4.   
  5. 如果介于A[i]之间,我们会找到一个最大结束元素小于A[i]的列表。按A[i]克隆并扩展此列表。我们将丢弃与此修改列表长度相同的所有其他列表。
  6.   

为:

  
      
  1. 如果A[i] 小于活动列表中所有最终候选者中的最小者,我们将启动长度为1的新活动列表。
  2.   
  3. 如果A[i]在所有活动列表的最终候选者中最大,我们将克隆最大的活动列表,并按A[i]进行扩展。
  4.   
  5. 如果介于A[i]之间,我们会找到一个列表,其最大结尾元素小于或等于 A[i]。按A[i]克隆并扩展此列表。我们将丢弃与此修改列表长度相同的所有其他列表。
  6.   

示例序列的第四步应该是:

10不小于10(最小元素)。我们找到小于或等于10的最大元素(即s[0]==10)。按10克隆并扩展此列表。放弃现有的长度为2的列表。新的s变为{10 10}

答案 1 :(得分:3)

除了binary_search()函数中的问题之外,您的代码几乎可以工作,此函数应该返回大于目标元素(x)的第一个元素的索引,因为您需要最长的非递减序列。修改它,它会没事的。

如果您使用c ++,std::lower_bound()std::upper_bound()将帮助您摆脱这个令人困惑的问题。顺便说一句,if语句“if (height[s[k]] >= height[i])”是多余的。

int binary_search(int first, int last, int x) {

    while(last > first)
    {
        int mid = first + (last - first) / 2;
        if(height[s[mid]] > x)
            last = mid;
        else
            first = mid + 1;
    }

    return first; /* or last */
}

答案 2 :(得分:2)

通过使用词典比较,将最长的增加子序列算法应用于有序对(A [i],i)。

答案 3 :(得分:1)

这个问题的完全不同的解决方案如下。制作数组的副本并对其进行排序。然后,计算数组中任意两个元素之间的最小非零差异(这将是两个相邻数组元素之间的最小非零差异),并将其称为δ。此步骤需要时间O(n log n)。

关键观察是,如果向原始数组的元素0添加0,向原始数组的第二个元素添加δ/ n,向数组的第三个元素添加2δ/ n等,则任何非递减序列在原始数组中,在新数组中变为严格增加的序列,反之亦然。因此,您可以通过这种方式转换数组,然后运行标准的最长增长子序列求解器,它在时间O(n log n)内运行。此过程的最终结果是用于查找最长非递减子序列的O(n log n)算法。

例如,考虑30,20,20,10,10,10,10。在这种情况下δ= 10且n = 7,因此δ/n≈1.42。然后新阵列

40, 21.42, 22.84, 14.28, 15.71, 17.14, 18.57

这里,LIS是14.28,15.71,17.14,18.57,它们在原始数组中映射回10,10,10,10。

希望这有帮助!

答案 4 :(得分:0)

我的Java版本:

  public static int longestNondecreasingSubsequenceLength(List<Integer> A) {
    int n = A.size();
    int dp[] = new int[n];
    int max = 0;
    for(int i = 0; i < n; i++) {
        int el = A.get(i);
        int idx = Arrays.binarySearch(dp, 0, max, el);
        if(idx < 0) {
            idx = -(idx + 1);
        }
        if(dp[idx] == el) { // duplicate found, let's find the last one 
            idx = Arrays.binarySearch(dp, 0, max, el + 1);
            if(idx < 0) {
                idx = -(idx + 1);
            }
        }
        dp[idx] = el;
        if(idx == max) {
            max++;
        }
    }
    return max;
}