Problem overview
You get an IntArray named nums and an integer k. The function kSmallest must return the k smallest values as a new IntArray. The order of the result doesn't matter.
For nums = [5, 9, 3, 6, 2, 1, 3, 2, 7, 5] and k = 4, the sorted array is [1, 2, 2, 3, 3, 5, 5, 6, 7, 9]. Its first four values are 1, 2, 2, 3, so that is the answer. Duplicates count separately: the value 2 appears twice in the result because it appears twice in the input.
Approach
The direct way to find the smallest value is to scan the array. Finding the second smallest needs another scan, and so on, which costs O(n * k) and gets slow when k is large.
Sorting removes the repeated scans. Once the array is in ascending order, the k smallest values are exactly the first k positions. The cost is one sort, O(n log n), plus copying k values. Sorting in place needs no extra array, which fits the lesson's note of O(n log n) time with O(1) extra space. The result array of size k is the output itself, not working memory. Faster ways exist, and the other lessons on this problem cover them.
Why does a prefix of the sorted array work? Every value after position k - 1 is greater than or equal to the value at k - 1, so no value left out can be smaller than one we keep. When several values tie at the boundary, any of them is a valid choice, and the problem accepts any order, so there is nothing more to decide.
Step by step
- Sort
numsin place. Callnums.sort(). On anIntArrayit rearranges the array itself and returns nothing. That is what we want. The similar-lookingsorted()returns a newList<Int>, which is a different type from theIntArraythe function must return. - Copy the first
kelements.copyOfRange(0, k)creates a newIntArrayholding the indices0tok - 1. The end index is exclusive, so passingkgives exactlykvalues.
The solution in Kotlin
class Solution {
fun kSmallest(nums: IntArray, k: Int): IntArray {
nums.sort()
return nums.copyOfRange(0, k)
}
}
Some Kotlin details to know:
- Function parameters are read-only names, so you can't write
nums = something. The contents of anIntArraycan still change, which is whynums.sort()compiles. The caller's array is sorted too, because both refer to the same array. copyOfRangereturns a new array of the same type. Changing the result later doesn't touchnums.- If
kis larger thannums.size,copyOfRange(0, k)throws anIndexOutOfBoundsException. The lesson's inputs keepkwithin the array.
Remember that nums is changed as a side effect. If the caller still needs the original order, make a copy first with nums.copyOf() and sort the copy. That copy costs O(n) extra space, which is why the lesson's version sorts in place.
Complexity
- Time:
O(n log n)for the sort, plusO(k)for the copy. Sincek <= n, the sort dominates. - Space:
O(1)extra beyond the returned array, because the sort works in place.
Edge cases and common mistakes
- Using
sorted()instead ofsort().nums.sorted()gives aList<Int>, and callingcopyOfRangeonnumsafterwards would copy the still-unsorted array, returning the wrong values. - Off-by-one in the end index.
copyOfRange(0, k - 1)returns onlyk - 1values. - Duplicates. Don't remove them. With
[2, 2, 2]andk = 2the answer is[2, 2]. k = 0.copyOfRange(0, 0)returns an empty array.k = nums.size. The result is the whole sorted array.
Testing
kSmallest(intArrayOf(5, 9, 3, 6, 2, 1, 3, 2, 7, 5), 4)returns[1, 2, 2, 3].kSmallest(intArrayOf(3, 1, 2), 3)returns[1, 2, 3].kSmallest(intArrayOf(3, 1, 2), 0)returns an empty array.