funcabs(x int)int { if x < 0 { return -x } return x }
复杂度分析
时间复杂度:O(n \log n),其中 n 是数组 arr 的长度。排序需要 O(n \log n)。
空间复杂度:O(\log n)。返回值不计算时间复杂度。排序需要 O(\log n) 的栈空间。
方法二:二分查找 + 双指针
假设数组长度为 n,注意到数组 arr 已经按照升序排序,我们可以将数组 arr 分成两部分,前一部分所有元素 [0, \textit{left}] 都小于 x,后一部分所有元素 [\textit{right}, n - 1] 都大于等于 x,left 与 right 都可以通过二分查找获得。
left 和 right 指向的元素都是各自部分最接近 x 的元素,因此我们可以通过比较 left 和 right 指向的元素获取整体最接近 x 的元素。如果 x - \textit{arr}[\textit{left}] \le \textit{arr}[\textit{right}] - x,那么将 left 减一,否则将 right 加一。相应地,如果 left 或 right 已经越界,那么不考虑对应部分的元素。
classSolution: deffindClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: right = bisect_left(arr, x) left = right - 1 for _ inrange(k): if left < 0: right += 1 elif right >= len(arr) or x - arr[left] <= arr[right] - x: left -= 1 else: right += 1 return arr[left + 1: right]
[sol2-C++]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution { public: vector<int> findClosestElements(vector<int>& arr, int k, int x){ int right = lower_bound(arr.begin(), arr.end(), x) - arr.begin(); int left = right - 1; while (k--) { if (left < 0) { right++; } elseif (right >= arr.size()) { left--; } elseif (x - arr[left] <= arr[right] - x) { left--; } else { right++; } } returnvector<int>(arr.begin() + left + 1, arr.begin() + right); } };
publicclassSolution { public IList<int> FindClosestElements(int[] arr, int k, int x) { int right = BinarySearch(arr, x); int left = right - 1; while (k-- > 0) { if (left < 0) { right++; } elseif (right >= arr.Length) { left--; } elseif (x - arr[left] <= arr[right] - x) { left--; } else { right++; } } IList<int> ans = new List<int>(); for (int i = left + 1; i < right; i++) { ans.Add(arr[i]); } return ans; }
publicintBinarySearch(int[] arr, int x) { int low = 0, high = arr.Length - 1; while (low < high) { int mid = low + (high - low) / 2; if (arr[mid] >= x) { high = mid; } else { low = mid + 1; } } return low; } }