【问题标题】:How to return Kth smallest/largest element from an array using quicksort's pivot?如何使用快速排序的枢轴从数组中返回第 K 个最小/最大元素?
【发布时间】:2021-07-25 13:02:03
【问题描述】:

我正在尝试在数组中找到第 K 个最小的元素 => val a = Array[Int](10, 1, 7, 2, 25, 11, 14)

为此,我使用了快速排序算法的枢轴机制。基于 Pivot 位置,我从start to pivot positionpivot+1 position to the end of the array 递归调用pivot 方法。

下面是我写的逻辑。

object KthLargestSmallestOfArray extends App {
  val a = Array[Int](10, 1, 7, 2, 25, 11, 14)
  val k = 3
  println(kthSmallest(a, 0, a.length-1, k))

  def partition(arr: Array[Int], low: Int, high: Int): Int = {
    val pivot = high
    var i = low - 1
    for(j <- low until high) {
      if(arr(j)<arr(pivot)) {
        i += 1
        swap(arr, i, j)
      }
    }
    swap(arr, i+1, high)
    i+1
  }

  def swap(arr: Array[Int], l: Int, h: Int): Unit = {
    val temp = arr(l)
    arr(l) = arr(h)
    arr(h) = temp
  }

  @tailrec
  def kthSmallest(arr: Array[Int], p: Int, q: Int, index: Int): Int = {
    if(p==q && p==index) {
        arr(p)
    } else {
      val r = partition(arr, p, q)
      if(r == k) {
        arr(r)
      } else if(r < k) {
        kthSmallest(arr, r+1, q, k)
      } else kthSmallest(arr, p, r-1, k)
    }
  }
}

当我运行代码时,输​​入 k = 3,代码将 10 作为输出。

如果我对数组的元素进行排序 => (1, 2, 7, 10, 11, 14, 25),7 是第三大元素。 数组有 10 作为它的第 4 个元素(0 到 3..6),并且数组(k)是 10,因此它返回 10。 如果我将 k 提供为 4,则输出为 11,依此类推。

无论如何我可以返回 7 而不是 10 吗?因为 7 是从头开始的第三小的元素,它与我提供的 k 的值匹配。

谁能告诉我我在哪里犯了错误,我该如何纠正它?

【问题讨论】:

  • 您了解 QuickSelect 算法吗? en.wikipedia.org/wiki/….
  • 我知道,但我不能使用分区进行排序。相反,我只需要返回 K 的值
  • 这不是你要找的错误,但是注意在kthSmallest方法中使用index而不是k(或者只是将index重命名为k),否则您将使用本地范围之外的值而不是参数)。
  • 刚刚注意到@trincot 的答案中已经报告了这一点。

标签: arrays algorithm scala


【解决方案1】:

我不精通 scala,但我发现了两个潜在问题:

  • 该函数有一个名为index 的参数,但您在函数体中继续引用k。这并不是一个真正的错误,因为除了原始 k 值之外,您永远不会传递任何其他值,但它看起来很糟糕,所以最好将该参数的名称更改为 k。真正的问题是下一个:

  • 由于k 确实是从 1 开始的位置,而 pq 是从 0 开始的索引,您应该调整 @ 之间的比较987654328@ 和 k(或 index)相应地。您正在寻找匹配 (r == k - 1)(在两个地方),并将 (r &lt; k) 更改为 (r &lt; k - 1)

【讨论】:

    【解决方案2】:

    @trincot 是正确的:

    • 当您的意思是引用 index 时,kthSmallest() 方法引用 k
    • 最小的在索引0,第二小的在索引1,等等。所以kth最小的在索引k-1

    但还有更多:

    • 如果index 超出数组arr 的范围,您的代码将引发运行时错误。也许这是设计使然,但有更好的处理方法。
    • 代码具有使arr 数组处于不确定状态的不良副作用。它可能是排序的,也可能是半排序的(即只是随机排序的)。这是一个等待发生的错误。

    但这里真正的问题是它不是 Scala。您正在使用 Scala 语言编写 C 代码。

    Scala 拥有丰富的标准库,可以实现更简洁的编码。例如,您可以使用PriorityQueue,它只会根据需要对其元素进行排序以检索下一个优先级项目。

    def kthSmallest[A:Ordering](arr: Array[A], idx: Int): A = {
      val rpq = mutable.PriorityQueue(arr:_*).reverse
      Iterator.continually(rpq.dequeue())
              .drop(idx min arr.length-1)
              .next()
    }
    

    如果你热衷于使用分区方法,为什么不直接partition()Array

    @annotation.tailrec
    def kthSmallest[A:Ordering](arr: Array[A], idx: Int): A = {
      import Ordering.Implicits._
      if (idx > arr.length-2) arr.max
      else if (idx < 1)       arr.min
      else arr.partition(_ < arr.head) match {
        case (Array(), b) => kthSmallest(b.tail, idx-1)
        case (a, b) => if (a.length > idx) kthSmallest(a, idx)
                       else kthSmallest(b, idx - a.length)
      }
    }
    

    这完全取决于这段代码应该服务于什么目的。如果你通过一些简单的练习来学习这门语言,那么到目前为止,你错过了 Scala 的组成部分。

    【讨论】:

      猜你喜欢
      • 2021-09-05
      • 1970-01-01
      • 2021-01-06
      • 1970-01-01
      • 2017-06-23
      • 2017-01-21
      • 2021-11-28
      • 2023-03-04
      • 2017-03-04
      相关资源
      最近更新 更多