Arrays 如何在未排序的只读数组中查找第k个最小整数?
这是一个标准问题,已在多个站点中多次得到回答,但在此版本中存在其他限制:Arrays 如何在未排序的只读数组中查找第k个最小整数?,arrays,algorithm,Arrays,Algorithm,这是一个标准问题,已在多个站点中多次得到回答,但在此版本中存在其他限制: 数组是只读的(我们无法修改数组) 在O(1)空间中进行 有人能以尽可能最佳的时间复杂度向我解释一下解决这个问题的方法吗。我假设只读数组是一个很强的要求,并尝试在这个答案中找到不违反它的折衷办法(因此,使用不是一个选项,因为它会修改数组) 作为旁注,从中,如果不修改数组,则无法在O(1)空间中执行此操作: 选择所需的空间复杂度很容易被认为是k+ O(1)(或n)− k(如果k>n/2),就地算法可以选择 仅O(1)个附加存储
有人能以尽可能最佳的时间复杂度向我解释一下解决这个问题的方法吗。我假设只读数组是一个很强的要求,并尝试在这个答案中找到不违反它的折衷办法(因此,使用不是一个选项,因为它会修改数组) 作为旁注,从中,如果不修改数组,则无法在O(1)空间中执行此操作: 选择所需的空间复杂度很容易被认为是k+ O(1)(或n)− k(如果k>n/2),就地算法可以选择 仅O(1)个附加存储。。。。空间复杂度可以降低 以获得近似答案或正确答案为代价 有一定的可能性 它可以用O(nlogk)时间在O(k)空间中完成。如果
k
为常数,则为O(1)
解
这样做的目的是保持最大大小k
。首先用第一个k
元素填充它,然后继续迭代数组。如果某个元素x
小于堆的顶部,则弹出旧的头,然后插入x
完成后,堆的头是k
th最小的元素
就大O表示法而言,可以在
O(n)
中使用O(k)
space完成,方法是使用大小为2k
的新数组,将元素分块加载到数组中,并在此辅助数组上使用以查找k
第个元素。丢弃所有大于第k个元素的元素,并为下一个块重复(加载更多k
元素)。复杂性是O(k*n/k)=O(n)
然而,这种方法很少使用,并且其常数比堆解决方案(经常使用)要差得多
如果您真的想使用O(1)空间,可以通过查找最小
k
次来使用蛮力解决方案。您只需要记住旧的最小值,即常量空间。此解决方案是O(nk)
时间和O(1)空间,在时间方面,其效率明显低于替代方案。在O(n log d)时间复杂度和O(1)空间复杂度中,实际上有一种方法可以解决此问题,而无需修改数组。这里,n表示数组的长度,d表示数组中包含的数字范围的长度
其思想是为第k个最小元素执行一个。从lo=最小元素和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;
}
public int kthsmallest(最终列表a,int k){
如果(a==null | | a.size()==0)
抛出新的IllegalArgumentException(“空或空列表”);
int lo=集合的最小值(a);
int hi=集合的最大值(a);
当(lo=k)断裂时;
}
if(不计其数=k){
中途返回;
}else if(不计其数>=k){
hi=mid-1;
}否则{
lo=中间+1;
}
}
assert false:“k不能大于列表的大小。”;
返回-1;
}
请注意,此解决方案也适用于具有重复和/或负数的数组。我在InterviewBit上看到了此问题,我想我的解决方案是正确的,捕获了我在下面的数组中查找第k个最小元素所遵循的逻辑
- 找到数组中最小的元素,这是最后一个最小的元素李>
- 在循环中,对于“k”次,找到在循环的最后一次迭代中找到的最小元素之后的最大元素(在第一步中找到的最后一个最小元素将在循环的第一次迭代中使用)
- 返回循环中最后找到的下一个最大元素 复制了下面的代码
int-getminister(常量int*A,int-nSize){
int-nsmalletemindex=-1;
int i;
对于(i=0;i
我在我的机器上为测试用例执行并验证了它,但它在Interviewbit上不被接受
如果解决方案通过你的验证,我会很高兴的。让我知道你的意见。是
k
常数吗?它可以在O(k)空间中完成,而不需要修改好主意的列表,但我同意@amit的观察。在O(k)空间中执行此操作很简单,但我怀疑仅在O(1)空间中执行此操作是否可行,因为无法更改数组。@amit:k是作为输入提供的。它可以介于1和n之间。是的,在O(nk)时间内使用蛮力方法即可。但是w
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;
}