检测几乎排序的数组中的未排序元素

时间:2016-01-15 11:05:34

标签: algorithm sorting

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

  

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

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

3 个答案:

答案 0 :(得分:3)

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

然而,你的第二个案例使这样做成为一个问题。我将假设您始终希望保留数字列表中增长最长的子序列(如第二种情况)。

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

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

M[j]存储最小值k的索引X[k],以便在j上以X[k]结尾的长度为k ≤ i的子序列不断增加范围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 始终不会减少。

GOOGLE_CLIENT_ID

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

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

答案 1 :(得分:1)

仅检测

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

但它含糊不清:在清单2中,出了什么问题? 30/31,或21 /.../ 25?

如果错误的数字被隔离,您只需删除它们即可。但是,如果你有2个数字,该怎么办?您必须定义更多规则。

检测并排序:

<强>复杂度:

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

如果有一个未排序的元素,则需要使用log N在适当的位置替换它(如果我认为其他所有内容都已排序,并且在二进制树中的ad hoc结构中)。

它有k个未排序的元素,它将需要k log N。

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

如果一切混乱,N log N,这是排序的经典复杂性。

<强>算法:

因此,最简单的算法是迭代,并在平衡树中插入好位置。这是一种插入。

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

答案 2 :(得分:-1)

我认为这对你有用。它找到最长的子序列,然后清除其他元素。实现在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
}