【问题标题】:Detecting unsorted elements in an almost sorted array在几乎排序的数组中检测未排序的元素
【发布时间】:2016-01-15 11:05:33
【问题描述】:

我有一组已排序的样本,但由于数据中的错误,有时未排序的值会蔓延。我需要检测这些值并删除它们。我将在下面展示一些示例数据集。

20 30 21 22 23 24 25

30 31 21 22 23 24 25

30 21 22 23 24 25 26

20 21 22 23 18 25 26

20 15 21 22 23 24 25

在每种情况下,粗斜体数字都是应该删除的。删除这些数字/检测这些数字的索引的算法是什么?

【问题讨论】:

  • 我的问题是实现相同目标的算法是什么。如果有帮助,我可以解释一下我尝试过的一些方法
  • 从低索引到高索引读取数组(反之亦然,这几乎无关紧要)并丢弃(或以其他方式标记)乱序值。您不会找到复杂度低于O(n) 的通用方法。你还能想到什么?
  • @HighPerformanceMark 第二个测试用例失败。您将需要使用这种方法重复执行此操作,这不是 O(n)。
  • O(n) 是我正在寻找的。我想知道如何标记乱序元素?据我所知,不能使用 arr[k + 1]

标签: algorithm sorting


【解决方案1】:

检测相对简单,步骤更少 - 您可以在O(n) 时间内完成。只需遍历数组并将每个元素与下一个元素进行比较。您将能够找到(并标记索引或丢弃)乱序数字。

但是,您的第二种情况使这样做成为一个问题。我会假设您总是希望保留数字列表中最长的递增子序列(如第二种情况)。

您可以使用数组和二分搜索有效地解决这个问题。该算法对每个序列元素进行一次二分查找,其总时间可以表示为O(n log n)

按顺序处理序列元素,保持目前找到的最长递增子序列。将序列值表示为X[0], X[1]等。L表示迄今为止发现的最长递增子序列的长度。

M[j] 存储最小值X[k] 的索引k,这样在k ≤ i 范围内有一个长度增加的子序列jX[k] 结尾。 j ≤ k ≤ i 总是。 P[k]存储了X[k]的前身的索引在以X[k]结尾的最长递增子序列中

序列X[M[1]], X[M[2]], ..., X[M[L]] 在算法的所有点上总是不递减的。

P = array of length N
M = array of length N + 1 // Using a 1 indexed array for ease of understanding

L = 0
for i in range 0 to N-1:
   // Binary search
   lo = 1
   hi = L
   while lo ≤ hi:
       mid = ceil((lo+hi)/2)
       if X[M[mid]] < X[i]:
           lo = mid+1
       else:
           hi = mid-1

newL = lo

P[i] = M[newL-1]
M[newL] = i

if newL > L:
    L = newL

S = array of length L
k = M[L]
for i in range L-1 to 0:
    S[i] = X[k]
    k = P[k]

return S

这个伪代码可以在Wikipedia article上找到。

如果您确实希望将乱序元素保留在列表中,只需使用插入排序对数组进行排序。

【讨论】:

  • 请给出伪代码,以防出现单个未排序的数字。我无法迭代并执行“if(arr[k + 1]
  • if(arr[k-1] &lt; arr[k] &amp;&amp; arr[k] &gt; arr[k+1] || arr[k-1] &gt; arr[k] &amp;&amp; arr[k] &lt; arr[k+1]) throw arr[k] 单独处理角索引。
【解决方案2】:

仅检测

检查(检查每个元素和下一个元素)至少需要 N-1 个步骤。

但它是模棱两可的:在清单 2 中,有什么问题? 30/31,还是 21/.../25 ?

如果坏号码是孤立的,您只需将其删除。但是,如果您有 2 个数字,该怎么办?您必须定义更多规则。

检测和排序:

复杂性:

如果您的列表完美排序,则需要 N-1 步(检查每个元素和下一个元素)。

如果有 一个 未排序的元素,则需要 log N 才能在合适的位置替换它(如果我认为其他所有内容都已排序,并且在像二叉树这样的特殊结构中)。

如果有 k 个未排序的元素,则需要 k log N。

所以 N(检查)+ k log N(插入)。

如果一切都乱了套,N log N,这是排序的经典复杂度。

算法:

因此,最简单的算法是在平衡树中迭代并插入到合适的位置。它是按插入排序的。

就像smoothsort:https://en.wikipedia.org/wiki/Smoothsort

【讨论】:

  • @HenkHolterman - 是的 - 隐含地,我想 Set 是 binary 。我准确地回答了。
  • 如果只有一个未排序的元素,“N(check)”是什么?
  • @diAblo 不幸的是,即使有一个未排序,您也需要 N(实际上是 N-1)次迭代来检查它。
【解决方案3】:

我认为这应该适合你。它找到最长的子序列,然后清除其他元素。实现是在c#中

public static void Main() {
    int[][] dataList = {
                        new []{20,30,21,22,23,24,25},
                        new []{30,31,21,22,23,24,25},
                        new []{30,21,22,23,24,25,26},
                        new []{20,21,22,23,18,25,26},
                        new []{20,15,21,22,23,24,25}
                    };

    foreach (var data in dataList)
        DetectAndRemoveUnsorted(data);
}

/// <summary>
/// Assumes ascending data. You can adapt it for descending data too
/// </summary>
static void DetectAndRemoveUnsorted(IList<int> data) {
    // first pass: Find the outliers; rather find the correct sequence
    int startOfLongestSeq = 0, lenOfLongestSeq = 0;
    int startOfCurrSeq = 0, lenOfCurrSeq = 0;
    for (int i = 0; i < data.Count - 1; ++i) {
        if (data[i] > data[i + 1]) { // we are breaking the ascending order, so this is another sequence
            lenOfCurrSeq = i - startOfCurrSeq + 1;
            if (lenOfCurrSeq > lenOfLongestSeq) {
                lenOfLongestSeq = lenOfCurrSeq;
                startOfLongestSeq = startOfCurrSeq;
            }
            startOfCurrSeq = i + 1;
        }
    }

    lenOfCurrSeq = data.Count - startOfCurrSeq;
    if (lenOfCurrSeq > lenOfLongestSeq) {
        lenOfLongestSeq = lenOfCurrSeq;
        startOfLongestSeq = startOfCurrSeq;
    }


    // second pass: cleanup outliers

    // now we know which sequence is the largest
    // we should get rid of the other sequences
    for (int i = startOfLongestSeq - 1; i >= 0; --i)
        data[i] = -1; // Mark them as invalid. if you want, you can delete them as well

    for (int i = data.Count - 1; i >= startOfLongestSeq + lenOfLongestSeq; --i)
        data[i] = -1; // Mark them as invalid. if you want, you can delete them as well
}

【讨论】:

  • 投反对票,想解释一下解决方案有什么问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多