Skip to content

Commit 7f35c53

Browse files
authored
feat: add merge sort method for lc No.0315 (#5229)
1 parent b1d8547 commit 7f35c53

4 files changed

Lines changed: 422 additions & 0 deletions

File tree

solution/0300-0399/0315.Count of Smaller Numbers After Self/README.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,153 @@ func merge(arr []Pair, low, mid, high int) {
608608

609609
### 方法三:归并排序
610610

611+
在归并排序的合并阶段,当左侧元素 $\textit{left}[i] \leq$ 右侧元素 $\textit{right}[j]$ 时,
612+
613+
说明右侧恰好有 $j$ 个元素比 $\textit{left}[i]$ 小,因此将 $j$ 累加到左侧元素的计数中。
614+
615+
当右侧元素全部清空,其体现右侧所有元素均小于剩余左侧元素,累加右侧数组长度到每个剩余左侧元素的计数即可。
616+
617+
__注意__:C++做超大数组的归并排序时,有内存爆炸风险,需使用 __缓存数组__ 避免Memory Limit Exceeded。
618+
619+
#### 复杂度:
620+
621+
- 时间复杂度:$O(n \log n)$,归并排序的标准时间复杂度。
622+
- 空间复杂度:$O(n)$,递归栈的标准空间复杂度。
623+
624+
<!-- tabs:start -->
625+
626+
#### Python3
627+
628+
```python
629+
class Solution:
630+
def countSmaller(self, nums: list[int]) -> list[int]:
631+
self.right_smaller_counts = [0] * len(nums)
632+
633+
nums_indices = [(num, idx) for idx, num in enumerate(nums)]
634+
self.merge_sort(nums_indices)
635+
636+
return self.right_smaller_counts
637+
638+
def combine_arrays(
639+
self,
640+
left_nums_indices: list[tuple[int, int]],
641+
right_nums_indices: list[tuple[int, int]],
642+
) -> list[tuple[int, int]]:
643+
merged_nums_indices: list[tuple[int, int]] = []
644+
left_idx, right_idx = 0, 0
645+
646+
while left_idx < len(left_nums_indices) and right_idx < len(right_nums_indices):
647+
if left_nums_indices[left_idx][0] <= right_nums_indices[right_idx][0]:
648+
# Iterated left side element finalizes its right smaller count.
649+
left_num_idx = left_nums_indices[left_idx][1]
650+
self.right_smaller_counts[left_num_idx] += right_idx
651+
652+
merged_nums_indices.append(left_nums_indices[left_idx])
653+
left_idx += 1
654+
continue
655+
656+
merged_nums_indices.append(right_nums_indices[right_idx])
657+
right_idx += 1
658+
659+
while left_idx < len(left_nums_indices):
660+
# Iterated left side element finalizes its right smaller count.
661+
left_num_idx = left_nums_indices[left_idx][1]
662+
self.right_smaller_counts[left_num_idx] += len(right_nums_indices)
663+
664+
merged_nums_indices.append(left_nums_indices[left_idx])
665+
left_idx += 1
666+
667+
while right_idx < len(right_nums_indices):
668+
merged_nums_indices.append(right_nums_indices[right_idx])
669+
right_idx += 1
670+
671+
return merged_nums_indices
672+
673+
def merge_sort(self, nums_indices: list[tuple[int, int]]) -> list[tuple[int, int]]:
674+
if len(nums_indices) == 1:
675+
return nums_indices # Single element.
676+
677+
split_idx = len(nums_indices) // 2
678+
679+
left_nums_indices = self.merge_sort(nums_indices[:split_idx])
680+
right_nums_indices = self.merge_sort(nums_indices[split_idx:])
681+
682+
return self.combine_arrays(left_nums_indices, right_nums_indices)
683+
```
684+
685+
686+
#### C++
687+
688+
```cpp
689+
class Solution {
690+
private:
691+
vector<int> rightSmallerCounts;
692+
vector<pair<int, int>> buffer;
693+
694+
void combineArrays(
695+
vector<pair<int, int>>& numsIndices, int leftBound, int splitIdx, int rightBound) {
696+
// Left side array = numsIndices[leftBound: splitIdx].
697+
// Right side array = numsIndices[splitIdx: rightBound + 1].
698+
int leftIdx = leftBound, rightIdx = splitIdx;
699+
int bufferIdx = leftBound;
700+
701+
while (leftIdx < splitIdx && rightIdx <= rightBound) {
702+
if (numsIndices[leftIdx].first <= numsIndices[rightIdx].first) {
703+
// Iterated left side element finalizes its right smaller count.
704+
int leftNumIdx = numsIndices[leftIdx].second;
705+
rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx;
706+
707+
buffer[bufferIdx++] = numsIndices[leftIdx++];
708+
}
709+
710+
else
711+
buffer[bufferIdx++] = numsIndices[rightIdx++];
712+
}
713+
714+
while (leftIdx < splitIdx) {
715+
// Iterated left side element finalizes its right smaller count.
716+
int leftNumIdx = numsIndices[leftIdx].second;
717+
rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx;
718+
719+
buffer[bufferIdx++] = numsIndices[leftIdx++];
720+
}
721+
722+
while (rightIdx <= rightBound)
723+
buffer[bufferIdx++] = numsIndices[rightIdx++];
724+
725+
for (int idx = leftBound; idx <= rightBound; idx++)
726+
numsIndices[idx] = buffer[idx]; // Put buffer data back to original array.
727+
}
728+
729+
void mergeSort(vector<pair<int, int>>& numsIndices, int leftBound, int rightBound) {
730+
if (leftBound == rightBound) return; // Single element.
731+
732+
// Plus 1: ensure splitIdx > leftBound.
733+
int splitIdx = (leftBound + rightBound + 1) / 2;
734+
735+
mergeSort(numsIndices, leftBound, splitIdx - 1);
736+
mergeSort(numsIndices, splitIdx, rightBound);
737+
738+
combineArrays(numsIndices, leftBound, splitIdx, rightBound);
739+
}
740+
741+
public:
742+
vector<int> countSmaller(vector<int>& nums) {
743+
buffer.resize(nums.size()); // Against memory explosions.
744+
745+
vector<pair<int, int>> numsIndices(nums.size());
746+
for (int idx = 0; idx < nums.size(); idx++)
747+
numsIndices[idx] = {nums[idx], idx};
748+
749+
rightSmallerCounts.assign(nums.size(), 0);
750+
mergeSort(numsIndices, 0, nums.size() - 1);
751+
return rightSmallerCounts;
752+
}
753+
};
754+
```
755+
756+
<!-- tabs:end -->
757+
611758
<!-- solution:end -->
612759

613760
<!-- problem:end -->

solution/0300-0399/0315.Count of Smaller Numbers After Self/README_EN.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,4 +582,160 @@ func merge(arr []Pair, low, mid, high int) {
582582

583583
<!-- solution:end -->
584584

585+
<!-- solution:start -->
586+
587+
### Solution 3: Merge Sort
588+
589+
During the merge phase of merge sort, when a left element $\textit{left}[i] \leq \textit{right}[j]$,
590+
it means exactly $j$ elements on the right side are smaller than $\textit{left}[i]$,
591+
so we accumulate $j$ into the count of $\textit{left}[i]$.
592+
593+
Once all right elements are exhausted, all right elements are smaller than
594+
each remaining left element, so we accumulate the right array's full length
595+
into each remaining left element's count.
596+
597+
**Note:** In C++, merge sort on very large arrays may suffer Memory Limit Exceeded.
598+
Use a buffer array to avoid excessive memory allocations.
599+
600+
#### Complexity
601+
602+
- Time complexity: $O(n \log n)$, the standard time complexity for merge sort.
603+
- Space complexity: $O(n)$, the standard space complexity of recursion stack.
604+
605+
<!-- tabs:start -->
606+
607+
#### Python3
608+
609+
```python
610+
class Solution:
611+
def countSmaller(self, nums: list[int]) -> list[int]:
612+
self.right_smaller_counts = [0] * len(nums)
613+
614+
nums_indices = [(num, idx) for idx, num in enumerate(nums)]
615+
self.merge_sort(nums_indices)
616+
617+
return self.right_smaller_counts
618+
619+
def combine_arrays(
620+
self,
621+
left_nums_indices: list[tuple[int, int]],
622+
right_nums_indices: list[tuple[int, int]],
623+
) -> list[tuple[int, int]]:
624+
merged_nums_indices: list[tuple[int, int]] = []
625+
left_idx, right_idx = 0, 0
626+
627+
while left_idx < len(left_nums_indices) and right_idx < len(right_nums_indices):
628+
if left_nums_indices[left_idx][0] <= right_nums_indices[right_idx][0]:
629+
# Iterated left side element finalizes its right smaller count.
630+
left_num_idx = left_nums_indices[left_idx][1]
631+
self.right_smaller_counts[left_num_idx] += right_idx
632+
633+
merged_nums_indices.append(left_nums_indices[left_idx])
634+
left_idx += 1
635+
continue
636+
637+
merged_nums_indices.append(right_nums_indices[right_idx])
638+
right_idx += 1
639+
640+
while left_idx < len(left_nums_indices):
641+
# Iterated left side element finalizes its right smaller count.
642+
left_num_idx = left_nums_indices[left_idx][1]
643+
self.right_smaller_counts[left_num_idx] += len(right_nums_indices)
644+
645+
merged_nums_indices.append(left_nums_indices[left_idx])
646+
left_idx += 1
647+
648+
while right_idx < len(right_nums_indices):
649+
merged_nums_indices.append(right_nums_indices[right_idx])
650+
right_idx += 1
651+
652+
return merged_nums_indices
653+
654+
def merge_sort(self, nums_indices: list[tuple[int, int]]) -> list[tuple[int, int]]:
655+
if len(nums_indices) == 1:
656+
return nums_indices # Single element.
657+
658+
split_idx = len(nums_indices) // 2
659+
660+
left_nums_indices = self.merge_sort(nums_indices[:split_idx])
661+
right_nums_indices = self.merge_sort(nums_indices[split_idx:])
662+
663+
return self.combine_arrays(left_nums_indices, right_nums_indices)
664+
```
665+
666+
667+
#### C++
668+
669+
```cpp
670+
class Solution {
671+
private:
672+
vector<int> rightSmallerCounts;
673+
vector<pair<int, int>> buffer;
674+
675+
void combineArrays(
676+
vector<pair<int, int>>& numsIndices, int leftBound, int splitIdx, int rightBound) {
677+
// Left side array = numsIndices[leftBound: splitIdx].
678+
// Right side array = numsIndices[splitIdx: rightBound + 1].
679+
int leftIdx = leftBound, rightIdx = splitIdx;
680+
int bufferIdx = leftBound;
681+
682+
while (leftIdx < splitIdx && rightIdx <= rightBound) {
683+
if (numsIndices[leftIdx].first <= numsIndices[rightIdx].first) {
684+
// Iterated left side element finalizes its right smaller count.
685+
int leftNumIdx = numsIndices[leftIdx].second;
686+
rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx;
687+
688+
buffer[bufferIdx++] = numsIndices[leftIdx++];
689+
}
690+
691+
else
692+
buffer[bufferIdx++] = numsIndices[rightIdx++];
693+
}
694+
695+
while (leftIdx < splitIdx) {
696+
// Iterated left side element finalizes its right smaller count.
697+
int leftNumIdx = numsIndices[leftIdx].second;
698+
rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx;
699+
700+
buffer[bufferIdx++] = numsIndices[leftIdx++];
701+
}
702+
703+
while (rightIdx <= rightBound)
704+
buffer[bufferIdx++] = numsIndices[rightIdx++];
705+
706+
for (int idx = leftBound; idx <= rightBound; idx++)
707+
numsIndices[idx] = buffer[idx]; // Put buffer data back to original array.
708+
}
709+
710+
void mergeSort(vector<pair<int, int>>& numsIndices, int leftBound, int rightBound) {
711+
if (leftBound == rightBound) return; // Single element.
712+
713+
// Plus 1: ensure splitIdx > leftBound.
714+
int splitIdx = (leftBound + rightBound + 1) / 2;
715+
716+
mergeSort(numsIndices, leftBound, splitIdx - 1);
717+
mergeSort(numsIndices, splitIdx, rightBound);
718+
719+
combineArrays(numsIndices, leftBound, splitIdx, rightBound);
720+
}
721+
722+
public:
723+
vector<int> countSmaller(vector<int>& nums) {
724+
buffer.resize(nums.size()); // Against memory explosions.
725+
726+
vector<pair<int, int>> numsIndices(nums.size());
727+
for (int idx = 0; idx < nums.size(); idx++)
728+
numsIndices[idx] = {nums[idx], idx};
729+
730+
rightSmallerCounts.assign(nums.size(), 0);
731+
mergeSort(numsIndices, 0, nums.size() - 1);
732+
return rightSmallerCounts;
733+
}
734+
};
735+
```
736+
737+
<!-- tabs:end -->
738+
739+
<!-- solution:end -->
740+
585741
<!-- problem:end -->
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
class Solution {
2+
private:
3+
vector<int> rightSmallerCounts;
4+
vector<pair<int, int>> buffer;
5+
6+
void combineArrays(
7+
vector<pair<int, int>>& numsIndices, int leftBound, int splitIdx, int rightBound) {
8+
// Left side array = numsIndices[leftBound: splitIdx].
9+
// Right side array = numsIndices[splitIdx: rightBound + 1].
10+
int leftIdx = leftBound, rightIdx = splitIdx;
11+
int bufferIdx = leftBound;
12+
13+
while (leftIdx < splitIdx && rightIdx <= rightBound) {
14+
if (numsIndices[leftIdx].first <= numsIndices[rightIdx].first) {
15+
// Iterated left side element finalizes its right smaller count.
16+
int leftNumIdx = numsIndices[leftIdx].second;
17+
rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx;
18+
19+
buffer[bufferIdx++] = numsIndices[leftIdx++];
20+
}
21+
22+
else
23+
buffer[bufferIdx++] = numsIndices[rightIdx++];
24+
}
25+
26+
while (leftIdx < splitIdx) {
27+
// Iterated left side element finalizes its right smaller count.
28+
int leftNumIdx = numsIndices[leftIdx].second;
29+
rightSmallerCounts[leftNumIdx] += rightIdx - splitIdx;
30+
31+
buffer[bufferIdx++] = numsIndices[leftIdx++];
32+
}
33+
34+
while (rightIdx <= rightBound)
35+
buffer[bufferIdx++] = numsIndices[rightIdx++];
36+
37+
for (int idx = leftBound; idx <= rightBound; idx++)
38+
numsIndices[idx] = buffer[idx]; // Put buffer data back to original array.
39+
}
40+
41+
void mergeSort(vector<pair<int, int>>& numsIndices, int leftBound, int rightBound) {
42+
if (leftBound == rightBound) return; // Single element.
43+
44+
// Plus 1: ensure splitIdx > leftBound.
45+
int splitIdx = (leftBound + rightBound + 1) / 2;
46+
47+
mergeSort(numsIndices, leftBound, splitIdx - 1);
48+
mergeSort(numsIndices, splitIdx, rightBound);
49+
50+
combineArrays(numsIndices, leftBound, splitIdx, rightBound);
51+
}
52+
53+
public:
54+
vector<int> countSmaller(vector<int>& nums) {
55+
buffer.resize(nums.size()); // Against memory explosions.
56+
57+
vector<pair<int, int>> numsIndices(nums.size());
58+
for (int idx = 0; idx < nums.size(); idx++)
59+
numsIndices[idx] = {nums[idx], idx};
60+
61+
rightSmallerCounts.assign(nums.size(), 0);
62+
mergeSort(numsIndices, 0, nums.size() - 1);
63+
return rightSmallerCounts;
64+
}
65+
};

0 commit comments

Comments
 (0)