【问题标题】:K'th smallest element in an array数组中第 K 个最小的元素
【发布时间】:2021-09-09 19:36:54
【问题描述】:

给定一个大小为 n 的数组,给出一个使用 O(1) 空间(不是中位数的中位数)的确定性算法(不是快速排序),它将找到第 K 个最小的项目。

有一些显而易见的解决方案,例如在 nlogn 上对数组进行排序或在 nk 中找到最小 k 时间,但我确信有更好的方法来做到这一点。它不一定是班轮(我怀疑它是否可以)。

感谢帮助。

【问题讨论】:

  • 我说过它不能是中位数或快速排序,因为两者都不是确定性的或使用额外的空间。
  • 合并排序和堆排序是确定性的,在O(n log n)中运行。
  • 但不在 O(1) 空间中 - @blazs

标签: arrays algorithm


【解决方案1】:

排列(以免浪费额外空间)将数组放入min-heap (can be built in O(n) time),然后执行k extract-min 操作,每个操作都采用O(log n)时间。所以你有O(n + k*log n) 的时间。 (因为k <= n 最坏的情况是O(n log n)。)

空间复杂度为O(1),或者,如果算上我们修改过的数组,则为O(n);但任何算法都需要数组,因此需要数组贡献的O(n) 空间。堆产生的额外空间成本为O(1)

很明显,这种方法是正确的:第一个 extract-min 返回(并从堆中删除)最小的元素;第二个 extract-min 返回(并从堆中删除)第二小的元素; ...; k-th extract-min 返回(并从堆中删除)k-th 最小元素。

如果kn/2“大得多”,那么您可以使用max-heap 加快处理速度,并使用类似方法搜索(n-k)-th 最大元素。

【讨论】:

    【解决方案2】:

    我发现 BitVector 是解决此问题的另一种方法。

    填充 BitVector 需要 O(n) 时间,找到第 k 个最小的元素大约需要 O(k) 时间。因此,总复杂度将是 O(n)。

    空间复杂度可能因每种情况而异。在我的场景中,列表可以包含一组未排序的正整数,从 1 到 2147483647 (java.lang.Integer.MAX_VALUE) (2147483647/32 * 4 / 1024 / 1024 =~ 255 MB)。

    这是我在 Java 中的实现:

    int findKthSmallestInUnsortedList(List<Integer> list, int k) {
        // Step 1: Populate (read) data from list to the bit-vector in O(n) time
        int[] bitVector = new int[Integer.MAX_VALUE/32]; //4 bytes chunks (int): 32 bits
        for(Integer num : list) {
            int chunkNum = num / 32;
            int indexInChunk = num % 32;
            bitVector[chunkNum] |= (1 << indexInChunk);
        }
    
        // Step 2: Find the k'th '1' starting from the first chunk in O(k) time
        int count = 0;
        for (int i = 0; i < bitVector.length; i++) {
            int bitCount = 0;
            while(bitVector[i] != 0) {
                if ((bitVector[i] & 1) != 0) {
                    count++;
                }
                if (count == k) {
                    return i * 32 + bitCount;
                }
                bitCount++;
                bitVector[i] >>= 1;
            }
        }
        return -1; // not found
    }
    

    对于不熟悉BitVector的朋友,这里举个例子:

    想象数字 4 在列表中。因此我们将 first 块中的 4th 位设置为 1:

    00000000 00000000 00000000 00001000

    如果读取到33,按照上面的实现,我们去second块,将first位设置为1,以此类推。

    最后,我们从 BitVector 的开头开始计数 k '1'。当找到第 k 个 1 时,我们将那个 1 的块号乘以 32,并添加这个 1 在那个块中的位置(i * 32 + bitCount)。

    【讨论】:

      【解决方案3】:

      使用二分搜索的第 K 个最小元素 :)

      bool c(int a[],int l,int r,int v,int k)
      {
          int cnt=0;
          for(int i=l;i<=r;i++)
          {
              
              if(a[i]<=v )
              {
                  cnt++;
              }
          }
          if(cnt>=k)
          {
              return true;
          }
          return false;
      }
      int kthSmallest(int a[], int l1, int r1, int k)
      {
          int l=a[l1],r=a[l1];
          for(int i=l1+1;i<r1;i++)
          {
              l=min(l,a[i]);
              r=max(r,a[i]);
          }
          int res=0;
       
          while(l<=r)
          {
              int mid=l+(r-l)/2;
              if(c(a,l1,r1,mid,k))
              {
                  res=mid;
                  r=mid-1;
              }else
              {
                  l=mid+1;
              }
          }
          return res;
      }
      

      【讨论】:

      • 时间复杂度=O(nlogn) , 空间复杂度=O(1)
      猜你喜欢
      • 2019-08-13
      • 1970-01-01
      • 1970-01-01
      • 2014-12-07
      • 1970-01-01
      • 2012-10-22
      • 2022-11-19
      • 2021-01-13
      • 2020-03-02
      相关资源
      最近更新 更多