From 24141ed747586bf60609ec9e6fb9915c002d6600 Mon Sep 17 00:00:00 2001 From: Jack Yao <105488074+StarsExpress@users.noreply.github.com> Date: Sun, 24 May 2026 19:41:12 +0800 Subject: [PATCH] Completed merge sort method for Q.315. --- .../README.md | 147 +++++++++++++++++ .../README_EN.md | 156 ++++++++++++++++++ .../Solution3.cpp | 65 ++++++++ .../Solution3.py | 54 ++++++ 4 files changed, 422 insertions(+) create mode 100644 solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.cpp create mode 100644 solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.py diff --git a/solution/0300-0399/0315.Count of Smaller Numbers After Self/README.md b/solution/0300-0399/0315.Count of Smaller Numbers After Self/README.md index 346d59dafe12a..c8b37bc358027 100644 --- a/solution/0300-0399/0315.Count of Smaller Numbers After Self/README.md +++ b/solution/0300-0399/0315.Count of Smaller Numbers After Self/README.md @@ -608,6 +608,153 @@ func merge(arr []Pair, low, mid, high int) { ### 方法三:归并排序 +在归并排序的合并阶段,当左侧元素 $\textit{left}[i] \leq$ 右侧元素 $\textit{right}[j]$ 时, + +说明右侧恰好有 $j$ 个元素比 $\textit{left}[i]$ 小,因此将 $j$ 累加到左侧元素的计数中。 + +当右侧元素全部清空,其体现右侧所有元素均小于剩余左侧元素,累加右侧数组长度到每个剩余左侧元素的计数即可。 + +__注意__:C++做超大数组的归并排序时,有内存爆炸风险,需使用 __缓存数组__ 避免Memory Limit Exceeded。 + +#### 复杂度: + +- 时间复杂度:$O(n \log n)$,归并排序的标准时间复杂度。 +- 空间复杂度:$O(n)$,递归栈的标准空间复杂度。 + + + +#### Python3 + +```python +class Solution: + def countSmaller(self, nums: list[int]) -> list[int]: + self.right_smaller_counts = [0] * len(nums) + + nums_indices = [(num, idx) for idx, num in enumerate(nums)] + self.merge_sort(nums_indices) + + return self.right_smaller_counts + + def combine_arrays( + self, + left_nums_indices: list[tuple[int, int]], + right_nums_indices: list[tuple[int, int]], + ) -> list[tuple[int, int]]: + merged_nums_indices: list[tuple[int, int]] = [] + left_idx, right_idx = 0, 0 + + while left_idx < len(left_nums_indices) and right_idx < len(right_nums_indices): + if left_nums_indices[left_idx][0] <= right_nums_indices[right_idx][0]: + # Iterated left side element finalizes its right smaller count. + left_num_idx = left_nums_indices[left_idx][1] + self.right_smaller_counts[left_num_idx] += right_idx + + merged_nums_indices.append(left_nums_indices[left_idx]) + left_idx += 1 + continue + + merged_nums_indices.append(right_nums_indices[right_idx]) + right_idx += 1 + + while left_idx < len(left_nums_indices): + # Iterated left side element finalizes its right smaller count. + left_num_idx = left_nums_indices[left_idx][1] + self.right_smaller_counts[left_num_idx] += len(right_nums_indices) + + merged_nums_indices.append(left_nums_indices[left_idx]) + left_idx += 1 + + while right_idx < len(right_nums_indices): + merged_nums_indices.append(right_nums_indices[right_idx]) + right_idx += 1 + + return merged_nums_indices + + def merge_sort(self, nums_indices: list[tuple[int, int]]) -> list[tuple[int, int]]: + if len(nums_indices) == 1: + return nums_indices # Single element. + + split_idx = len(nums_indices) // 2 + + left_nums_indices = self.merge_sort(nums_indices[:split_idx]) + right_nums_indices = self.merge_sort(nums_indices[split_idx:]) + + return self.combine_arrays(left_nums_indices, right_nums_indices) +``` + + +#### C++ + +```cpp +class Solution { +private: + vector rightSmallerCounts; + vector> buffer; + + void combineArrays( + vector>& numsIndices, int leftBound, int splitIdx, int rightBound) { + // Left side array = numsIndices[leftBound: splitIdx]. + // Right side array = numsIndices[splitIdx: rightBound + 1]. + int leftIdx = leftBound, rightIdx = splitIdx; + int bufferIdx = leftBound; + + while (leftIdx < splitIdx && rightIdx <= rightBound) { + if (numsIndices[leftIdx].first <= numsIndices[rightIdx].first) { + // Iterated left side element finalizes its right smaller count. + int leftNumIdx = numsIndices[leftIdx].second; + rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx; + + buffer[bufferIdx++] = numsIndices[leftIdx++]; + } + + else + buffer[bufferIdx++] = numsIndices[rightIdx++]; + } + + while (leftIdx < splitIdx) { + // Iterated left side element finalizes its right smaller count. + int leftNumIdx = numsIndices[leftIdx].second; + rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx; + + buffer[bufferIdx++] = numsIndices[leftIdx++]; + } + + while (rightIdx <= rightBound) + buffer[bufferIdx++] = numsIndices[rightIdx++]; + + for (int idx = leftBound; idx <= rightBound; idx++) + numsIndices[idx] = buffer[idx]; // Put buffer data back to original array. + } + + void mergeSort(vector>& numsIndices, int leftBound, int rightBound) { + if (leftBound == rightBound) return; // Single element. + + // Plus 1: ensure splitIdx > leftBound. + int splitIdx = (leftBound + rightBound + 1) / 2; + + mergeSort(numsIndices, leftBound, splitIdx - 1); + mergeSort(numsIndices, splitIdx, rightBound); + + combineArrays(numsIndices, leftBound, splitIdx, rightBound); + } + +public: + vector countSmaller(vector& nums) { + buffer.resize(nums.size()); // Against memory explosions. + + vector> numsIndices(nums.size()); + for (int idx = 0; idx < nums.size(); idx++) + numsIndices[idx] = {nums[idx], idx}; + + rightSmallerCounts.assign(nums.size(), 0); + mergeSort(numsIndices, 0, nums.size() - 1); + return rightSmallerCounts; + } +}; +``` + + + diff --git a/solution/0300-0399/0315.Count of Smaller Numbers After Self/README_EN.md b/solution/0300-0399/0315.Count of Smaller Numbers After Self/README_EN.md index 37954710272a2..4493998a8c052 100644 --- a/solution/0300-0399/0315.Count of Smaller Numbers After Self/README_EN.md +++ b/solution/0300-0399/0315.Count of Smaller Numbers After Self/README_EN.md @@ -582,4 +582,160 @@ func merge(arr []Pair, low, mid, high int) { + + +### Solution 3: Merge Sort + +During the merge phase of merge sort, when a left element $\textit{left}[i] \leq \textit{right}[j]$, +it means exactly $j$ elements on the right side are smaller than $\textit{left}[i]$, +so we accumulate $j$ into the count of $\textit{left}[i]$. + +Once all right elements are exhausted, all right elements are smaller than +each remaining left element, so we accumulate the right array's full length +into each remaining left element's count. + +**Note:** In C++, merge sort on very large arrays may suffer Memory Limit Exceeded. +Use a buffer array to avoid excessive memory allocations. + +#### Complexity + +- Time complexity: $O(n \log n)$, the standard time complexity for merge sort. +- Space complexity: $O(n)$, the standard space complexity of recursion stack. + + + +#### Python3 + +```python +class Solution: + def countSmaller(self, nums: list[int]) -> list[int]: + self.right_smaller_counts = [0] * len(nums) + + nums_indices = [(num, idx) for idx, num in enumerate(nums)] + self.merge_sort(nums_indices) + + return self.right_smaller_counts + + def combine_arrays( + self, + left_nums_indices: list[tuple[int, int]], + right_nums_indices: list[tuple[int, int]], + ) -> list[tuple[int, int]]: + merged_nums_indices: list[tuple[int, int]] = [] + left_idx, right_idx = 0, 0 + + while left_idx < len(left_nums_indices) and right_idx < len(right_nums_indices): + if left_nums_indices[left_idx][0] <= right_nums_indices[right_idx][0]: + # Iterated left side element finalizes its right smaller count. + left_num_idx = left_nums_indices[left_idx][1] + self.right_smaller_counts[left_num_idx] += right_idx + + merged_nums_indices.append(left_nums_indices[left_idx]) + left_idx += 1 + continue + + merged_nums_indices.append(right_nums_indices[right_idx]) + right_idx += 1 + + while left_idx < len(left_nums_indices): + # Iterated left side element finalizes its right smaller count. + left_num_idx = left_nums_indices[left_idx][1] + self.right_smaller_counts[left_num_idx] += len(right_nums_indices) + + merged_nums_indices.append(left_nums_indices[left_idx]) + left_idx += 1 + + while right_idx < len(right_nums_indices): + merged_nums_indices.append(right_nums_indices[right_idx]) + right_idx += 1 + + return merged_nums_indices + + def merge_sort(self, nums_indices: list[tuple[int, int]]) -> list[tuple[int, int]]: + if len(nums_indices) == 1: + return nums_indices # Single element. + + split_idx = len(nums_indices) // 2 + + left_nums_indices = self.merge_sort(nums_indices[:split_idx]) + right_nums_indices = self.merge_sort(nums_indices[split_idx:]) + + return self.combine_arrays(left_nums_indices, right_nums_indices) +``` + + +#### C++ + +```cpp +class Solution { +private: + vector rightSmallerCounts; + vector> buffer; + + void combineArrays( + vector>& numsIndices, int leftBound, int splitIdx, int rightBound) { + // Left side array = numsIndices[leftBound: splitIdx]. + // Right side array = numsIndices[splitIdx: rightBound + 1]. + int leftIdx = leftBound, rightIdx = splitIdx; + int bufferIdx = leftBound; + + while (leftIdx < splitIdx && rightIdx <= rightBound) { + if (numsIndices[leftIdx].first <= numsIndices[rightIdx].first) { + // Iterated left side element finalizes its right smaller count. + int leftNumIdx = numsIndices[leftIdx].second; + rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx; + + buffer[bufferIdx++] = numsIndices[leftIdx++]; + } + + else + buffer[bufferIdx++] = numsIndices[rightIdx++]; + } + + while (leftIdx < splitIdx) { + // Iterated left side element finalizes its right smaller count. + int leftNumIdx = numsIndices[leftIdx].second; + rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx; + + buffer[bufferIdx++] = numsIndices[leftIdx++]; + } + + while (rightIdx <= rightBound) + buffer[bufferIdx++] = numsIndices[rightIdx++]; + + for (int idx = leftBound; idx <= rightBound; idx++) + numsIndices[idx] = buffer[idx]; // Put buffer data back to original array. + } + + void mergeSort(vector>& numsIndices, int leftBound, int rightBound) { + if (leftBound == rightBound) return; // Single element. + + // Plus 1: ensure splitIdx > leftBound. + int splitIdx = (leftBound + rightBound + 1) / 2; + + mergeSort(numsIndices, leftBound, splitIdx - 1); + mergeSort(numsIndices, splitIdx, rightBound); + + combineArrays(numsIndices, leftBound, splitIdx, rightBound); + } + +public: + vector countSmaller(vector& nums) { + buffer.resize(nums.size()); // Against memory explosions. + + vector> numsIndices(nums.size()); + for (int idx = 0; idx < nums.size(); idx++) + numsIndices[idx] = {nums[idx], idx}; + + rightSmallerCounts.assign(nums.size(), 0); + mergeSort(numsIndices, 0, nums.size() - 1); + return rightSmallerCounts; + } +}; +``` + + + + + diff --git a/solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.cpp b/solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.cpp new file mode 100644 index 0000000000000..c8605c5a8edd3 --- /dev/null +++ b/solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.cpp @@ -0,0 +1,65 @@ +class Solution { +private: + vector rightSmallerCounts; + vector> buffer; + + void combineArrays( + vector>& numsIndices, int leftBound, int splitIdx, int rightBound) { + // Left side array = numsIndices[leftBound: splitIdx]. + // Right side array = numsIndices[splitIdx: rightBound + 1]. + int leftIdx = leftBound, rightIdx = splitIdx; + int bufferIdx = leftBound; + + while (leftIdx < splitIdx && rightIdx <= rightBound) { + if (numsIndices[leftIdx].first <= numsIndices[rightIdx].first) { + // Iterated left side element finalizes its right smaller count. + int leftNumIdx = numsIndices[leftIdx].second; + rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx; + + buffer[bufferIdx++] = numsIndices[leftIdx++]; + } + + else + buffer[bufferIdx++] = numsIndices[rightIdx++]; + } + + while (leftIdx < splitIdx) { + // Iterated left side element finalizes its right smaller count. + int leftNumIdx = numsIndices[leftIdx].second; + rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx; + + buffer[bufferIdx++] = numsIndices[leftIdx++]; + } + + while (rightIdx <= rightBound) + buffer[bufferIdx++] = numsIndices[rightIdx++]; + + for (int idx = leftBound; idx <= rightBound; idx++) + numsIndices[idx] = buffer[idx]; // Put buffer data back to original array. + } + + void mergeSort(vector>& numsIndices, int leftBound, int rightBound) { + if (leftBound == rightBound) return; // Single element. + + // Plus 1: ensure splitIdx > leftBound. + int splitIdx = (leftBound + rightBound + 1) / 2; + + mergeSort(numsIndices, leftBound, splitIdx - 1); + mergeSort(numsIndices, splitIdx, rightBound); + + combineArrays(numsIndices, leftBound, splitIdx, rightBound); + } + +public: + vector countSmaller(vector& nums) { + buffer.resize(nums.size()); // Against memory explosions. + + vector> numsIndices(nums.size()); + for (int idx = 0; idx < nums.size(); idx++) + numsIndices[idx] = {nums[idx], idx}; + + rightSmallerCounts.assign(nums.size(), 0); + mergeSort(numsIndices, 0, nums.size() - 1); + return rightSmallerCounts; + } +}; \ No newline at end of file diff --git a/solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.py b/solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.py new file mode 100644 index 0000000000000..f130bc84864a0 --- /dev/null +++ b/solution/0300-0399/0315.Count of Smaller Numbers After Self/Solution3.py @@ -0,0 +1,54 @@ +class Solution: + def countSmaller(self, nums: list[int]) -> list[int]: + self.right_smaller_counts = [0] * len(nums) + + nums_indices = [(num, idx) for idx, num in enumerate(nums)] + self.merge_sort(nums_indices) + + return self.right_smaller_counts + + def combine_arrays( + self, + left_nums_indices: list[tuple[int, int]], + right_nums_indices: list[tuple[int, int]], + ) -> list[tuple[int, int]]: + merged_nums_indices: list[tuple[int, int]] = [] + left_idx, right_idx = 0, 0 + + while left_idx < len(left_nums_indices) and right_idx < len(right_nums_indices): + if left_nums_indices[left_idx][0] <= right_nums_indices[right_idx][0]: + # Iterated left side element finalizes its right smaller count. + left_num_idx = left_nums_indices[left_idx][1] + self.right_smaller_counts[left_num_idx] += right_idx + + merged_nums_indices.append(left_nums_indices[left_idx]) + left_idx += 1 + continue + + merged_nums_indices.append(right_nums_indices[right_idx]) + right_idx += 1 + + while left_idx < len(left_nums_indices): + # Iterated left side element finalizes its right smaller count. + left_num_idx = left_nums_indices[left_idx][1] + self.right_smaller_counts[left_num_idx] += len(right_nums_indices) + + merged_nums_indices.append(left_nums_indices[left_idx]) + left_idx += 1 + + while right_idx < len(right_nums_indices): + merged_nums_indices.append(right_nums_indices[right_idx]) + right_idx += 1 + + return merged_nums_indices + + def merge_sort(self, nums_indices: list[tuple[int, int]]) -> list[tuple[int, int]]: + if len(nums_indices) == 1: + return nums_indices # Single element. + + split_idx = len(nums_indices) // 2 + + left_nums_indices = self.merge_sort(nums_indices[:split_idx]) + right_nums_indices = self.merge_sort(nums_indices[split_idx:]) + + return self.combine_arrays(left_nums_indices, right_nums_indices)