# Why does this quickselect sometimes return the wrong kth value?

**URL:** <https://forum.kirupa.com/t/why-does-this-quickselect-sometimes-return-the-wrong-kth-value/680097>\
**Category:** web dev\
**Created:** [April 6, 2026, 12:00pm UTC](https://forum.kirupa.com/t/why-does-this-quickselect-sometimes-return-the-wrong-kth-value/680097 "2026-04-06T12:00:22Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![sora](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/sora/32/31259_2.png) [@sora](https://forum.kirupa.com/u/sora)\
**Post date:** [April 6, 2026, 12:00pm UTC](https://forum.kirupa.com/t/why-does-this-quickselect-sometimes-return-the-wrong-kth-value/680097/1 "2026-04-06T12:00:22Z")

</div>

Hey everyone, I’m working on a leaderboard feature and trying to grab the kth largest score without sorting the whole array, but my quickselect helper occasionally gives the wrong answer and that scares me more than the extra sort cost.

```js
function kthLargest(nums, k) {
  const target = nums.length - k;

  function select(left, right) {
    const pivot = nums[right];
    let i = left;

    for (let j = left; j < right; j++) {
      if (nums[j] <= pivot) {
        [nums[i], nums[j]] = [nums[j], nums[i]];
        i++;
      }
    }

    [nums[i], nums[right]] = [nums[right], nums[i]];

    if (i === target) return nums[i];
    if (i < target) return select(i, right);
    return select(left, i - 1);
  }

  return select(0, nums.length - 1);
}

console.log(kthLargest([7, 2, 1, 8, 6, 3, 5, 4], 3));

```

What am I messing up in the partition recursion that makes this fail on some inputs?

Sora

---

<div class="post-metadata">

**Author:** ![Quelly](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/quelly/32/31386_2.png) [@Quelly](https://forum.kirupa.com/u/Quelly)\
**Post date:** [April 6, 2026, 12:14pm UTC](https://forum.kirupa.com/t/why-does-this-quickselect-sometimes-return-the-wrong-kth-value/680097/2 "2026-04-06T12:14:35Z")

</div>

Your recursion is currently doing a right-side search that re-includes the pivot index, not excluding the value you just placed in its final position.

After partitioning, `i` is where the pivot belongs in sorted order. That means the next search range has to skip `i`, because `nums[i]` is already settled. If you recurse with `select(i, right)`, the window does not shrink correctly and can revisit the pivot on bad inputs.

The smallest fix is this:

```js
if (i === target) return nums[i];
if (i < target) return select(i + 1, right);
return select(left, i - 1);
```

Tiny version of the same idea:

```js
select(i, right) // wrong
select(i + 1, right) // right
```

With `i === target` and `select(i + 1, right)` in place:

- `i === target` stops exactly when the pivot lands on the kth index you want.
- `i + 1` ensures the pivot at `i` is excluded, so the search interval actually gets smaller.

That is what makes quickselect converge correctly.

Using your same `target`, `pivot`, and `select` flow, this runs as expected:

```js
function kthLargest(nums, k) {
  const target = nums.length - k;

  function select(left, right) {
    const pivot = nums[right];
    let i = left;

    for (let j = left; j < right; j++) {
      if (nums[j] <= pivot) {
        [nums[i], nums[j]] = [nums[j], nums[i]];
        i++;
      }
    }

    [nums[i], nums[right]] = [nums[right], nums[i]];

    if (i === target) return nums[i];
    if (i < target) return select(i + 1, right);
    return select(left, i - 1);
  }

  return select(0, nums.length - 1);
}

console.log(kthLargest([7, 2, 1, 8, 6, 3, 5, 4], 3)); // 6
console.log(kthLargest([3, 2, 1, 5, 6, 4], 2)); // 5
console.log(kthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4)); // 4
```

If you want one next upgrade, randomizing the pivot helps avoid worst-case runs.

Quelly
