Problem overview
The method kSmallest receives an array nums of positive integers and a number k. It must return the k smallest values, in any order. Duplicates count as separate values.
For nums = [5, 9, 3, 6, 2, 1, 3, 2, 7, 5] and k = 4, the values in increasing order are 1, 2, 2, 3, 3, 5, 5, 6, 7, 9. The first four are 1, 2, 2 and 3, so the answer is [1, 2, 2, 3]. Any ordering of those four values is accepted.
Approach: sort and take the front
If the array were sorted in ascending order, the smallest k values would simply be its first k elements. So the plan has two parts: sort, then cut off the first k elements.
Sorting costs O(n log n) time, which is the budget the lesson sets. Cutting the front is cheap next to it. Later lessons on the same problem get below O(n log n) with other tools, but sorting is the shortest correct solution.
Step by step
- Sort into a new array. A Swift function parameter is a constant, so
nums.sort()isn't allowed.nums.sorted()returns a sorted copy, which you store insortedNums. - Take the first
kelements. Subscript the sorted array with the range0..<k. The half-open range includes index 0 and stops before indexk, which gives exactlykelements. - Convert to an array. A range subscript gives an
ArraySlice, not an[Int]. The function returns[Int], so the slice is wrapped inArray(...).
The solution in Swift
class Solution {
func kSmallest(_ nums: [Int], _ k: Int) -> [Int] {
let sortedNums = nums.sorted()
return Array(sortedNums[0..<k])
}
}
The Swift details worth knowing:
sorted()returns a new array. The caller'snumsis unchanged. This is also why aletis enough forsortedNums.- An
ArraySliceis a view.sortedNums[0..<k]doesn't copy the elements. It points intosortedNumsand keeps its original indices, so a slice taken fromsortedNums[2..<5]starts at index 2, not 0. Wrapping it inArray(...)makes a standalone array whose indices start at 0. - Out-of-range slices crash. If
kis larger thannums.count, the range0..<kgoes past the end, and Swift stops the program with a run-time error. The lesson expectskto be a valid count. If you wanted to be safe,Array(sortedNums.prefix(k))returns the whole array whenkis too big. k = 0is fine. The range0..<0is empty, so the result is[].
Complexity
Time: O(n log n). The sort dominates. Building the slice and the array costs O(k), which is at most O(n).
Space: O(n). sorted() builds a second array of n elements, and the result holds k more. Because Swift arrays are values, sorting a var copy of the parameter in place would need an n-element copy too.
Edge cases and common mistakes
- Duplicates. Each copy of a value is a separate element. For
[2, 2, 1]withk = 2the answer is[1, 2], and for[2, 2, 1]withk = 3it is[1, 2, 2]. kequal to the length.0..<kcovers the whole array, and the result is the sorted array.- Using
0...k. The closed range includes indexkand returnsk + 1elements. It also crashes whenkequals the length. - Calling
sort()onnums. This doesn't compile, becausenumsis a constant. Swift reportscannot use mutating member on immutable value. - Returning the slice itself. A function declared to return
[Int]can't return anArraySlice<Int>. The compiler rejects it until you addArray(...).
Testing
[5, 9, 3, 6, 2, 1, 3, 2, 7, 5]withk = 4gives[1, 2, 2, 3].[4, 1]withk = 0gives[].[4, 1, 2]withk = 3gives[1, 2, 4].
