这是一个标准问题,已在多个网站中多次回答,但在此版本中还有其他限制:
有人可以在最佳的时间复杂性中向我解释这种方法。
答案 0 :(得分:23)
实际上有一种方法可以解决 O(n log d)时间复杂度& O(1)空间复杂度,无需修改数组。这里 n 代表数组的长度,而 d 是其中包含的数字范围的长度。
这个想法是为第k个最小元素执行binary search。从lo = minimum元素开始,hi =最大元素。在每个步骤中,检查有多少元素小于mid并相应地更新它。以下是我的解决方案的Java代码:
public int kthsmallest(final List<Integer> a, int k) {
if(a == null || a.size() == 0)
throw new IllegalArgumentException("Empty or null list.");
int lo = Collections.min(a);
int hi = Collections.max(a);
while(lo <= hi) {
int mid = lo + (hi - lo)/2;
int countLess = 0, countEqual = 0;
for(int i = 0; i < a.size(); i++) {
if(a.get(i) < mid) {
countLess++;
}else if(a.get(i) == mid) {
countEqual++;
}
if(countLess >= k) break;
}
if(countLess < k && countLess + countEqual >= k){
return mid;
}else if(countLess >= k) {
hi = mid - 1;
}else{
lo = mid + 1;
}
}
assert false : "k cannot be larger than the size of the list.";
return -1;
}
请注意,此解决方案也适用于具有重复和/或负数的数组。
答案 1 :(得分:11)
我将假设只读数组是一个强烈要求,并尝试在此答案中找到不违反它的权衡(因此,使用selection algorithm不是一个选项,因为它修改了数组)
作为旁注,来自wikipedia,如果不修改数组,就无法在O(1)空间中完成:
选择所需的空间复杂度很容易看出是k + O(1)(或n - k,如果k> n / 2),就地算法可以选择 只有O(1)额外的存储空间......空间复杂度可以降低 以获得近似答案或正确答案为代价 一定概率
可以在O(nlogk)时间的 O(k)空间中完成。如果k
是常量,则为O(1)
解决方案
我们的想法是保持k
的最大heap。首先使用第一个k
元素填充它,然后继续迭代数组。如果某个元素x
小于堆的顶部,则弹出旧头,然后插入x
。
完成后,堆的头部是k
最小的元素。
就大O符号而言,可以使用大小为O(n)
的新数组在 O(k)
2k
空间中完成,加载元素块到阵列,并在此辅助阵列上使用selection algorithm来查找k
元素。丢弃大于第k个元素的所有元素,并重复下一个块(加载更多k
个元素)。复杂性为O(k*n/k) = O(n)
然而,这很少使用,并且常常比堆解决方案更糟糕,堆解决方案经常使用。
如果您真的想使用O(1)空间,可以通过查找最少k
次来使用强力解决方案。你只需要记住旧的最小值,即恒定的空间。对于O(1)空间,此解决方案 O(nk)
时间,就时间而言,效率明显低于替代方案。
答案 2 :(得分:0)
我已经在InterviewBit上看到了这个问题,我想我已经解决了,捕获了我在下面的数组中找到第k个最小元素的逻辑。
int getSmallest(const int* A, int nSize){
int nSmallestElemIndex = -1;
int i;
for(i=0; i < nSize; i++){
if (-1 == nSmallestElemIndex){
nSmallestElemIndex = i;
continue;
}
if (A[i] < A[nSmallestElemIndex]){
nSmallestElemIndex = i;
}
}
return nSmallestElemIndex;
}
int nextBig(const int* A, int nSize, int nCurrentSmallest){
int nNextBig = -1;
int i;
for(i=0; i < nSize; i++){
if (i == nCurrentSmallest || A[i] < A[nCurrentSmallest]){
continue;
}
if (A[i] == A[nCurrentSmallest] &&
i <= nCurrentSmallest){
continue;
}
if (nNextBig == -1){
nNextBig = i;
continue;
}
if (A[i] >= A[nCurrentSmallest] && A[i] < A[nNextBig]){
nNextBig = i;
}
}
return nNextBig;
}
int kthsmallest(const int* A, int n1, int B) {
int nSmallestElem = getSmallest(A, n1);
int nCount = 1;
int nRes = nSmallestElem;
while(nCount < B){
nRes = nextBig(A, n1, nRes);
nCount++;
}
return nRes;
}
我在机器上的测试用例中执行并验证了它,但Interviewbit不接受它。
如果解决方案通过您的验证,我将感到高兴。让我知道您的评论。