我有一组已排序的样本,但由于数据中的错误,有时会出现未分类的值。我需要检测这些值并将其删除。我将在下面展示一些示例数据集。
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
在每种情况下,粗体斜体数字都应该被删除。什么算法可以删除这些数字/检测这些数字的索引?
答案 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
}