Skip to content

Commit 625d4e3

Browse files
committed
LeetCode 912. Sort an Array
1 parent 9b2a56c commit 625d4e3

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
315315
| [901. Online Stock Span][lc901] | 🟠 Medium | [![python](res/py.png)][lc901py] |
316316
| [904. Fruit Into Baskets][lc904] | 🟠 Medium | [![python](res/py.png)][lc904py] [![rust](res/rs.png)][lc904rs] |
317317
| [909. Snakes and Ladders][lc909] | 🟠 Medium | [![python](res/py.png)][lc909py] [![rust](res/rs.png)][lc909rs] |
318+
| [912. Sort an Array][lc912] | 🟠 Medium | [![python](res/py.png)][lc912py] [![rust](res/rs.png)][lc912rs] |
318319
| [916. Word Subsets][lc916] | 🟠 Medium | [![python](res/py.png)][lc916py] |
319320
| [918. Maximum Sum Circular Subarray][lc918] | 🟠 Medium | [![python](res/py.png)][lc918py] [![rust](res/rs.png)][lc918rs] |
320321
| [926. Flip String to Monotone Increasing][lc926] | 🟠 Medium | [![python](res/py.png)][lc926py] [![rust](res/rs.png)][lc926rs] |
@@ -1062,6 +1063,9 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
10621063
[lc909]: https://leetcode.com/problems/snakes-and-ladders/
10631064
[lc909py]: leetcode/snakes-and-ladders.py
10641065
[lc909rs]: leetcode/snakes-and-ladders.rs
1066+
[lc912]: https://leetcode.com/problems/sort-an-array/
1067+
[lc912py]: leetcode/sort-an-array.py
1068+
[lc912rs]: leetcode/sort-an-array.rs
10651069
[lc916]: https://leetcode.com/problems/word-subsets/
10661070
[lc916py]: leetcode/word-subsets.py
10671071
[lc918]: https://leetcode.com/problems/maximum-sum-circular-subarray/

leetcode/sort-an-array.py

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# 912. Sort an Array
2+
# 🟠 Medium
3+
#
4+
# https://leetcode.com/problems/sort-an-array/
5+
#
6+
# Tags: Array - Divide and Conquer - Sorting - Heap (Priority Queue)
7+
# - Merge Sort - Bucket Sort - Radix Sort - Counting Sort
8+
9+
import timeit
10+
from typing import List
11+
12+
13+
# Given the conditions of the problem description, merge sort seems to
14+
# be a good option, it won't use any extra memory and it guarantees
15+
# O(n*log(n)) time complexity.
16+
#
17+
# Time complexity: O(n*log(n))
18+
# Space complexity: O(n) - We use one copy of the input array to
19+
# alternately move elements from one to the other. The call stack will
20+
# be of height log(n), that is an extra O(log(n)).
21+
#
22+
# Runtime 1690 ms Beats 72.17%
23+
# Memory 21.4 MB Beats 90.91%
24+
class MergeSort:
25+
def sortArray(self, nums: List[int]) -> List[int]:
26+
# Define an internal function that sorts a section of the input
27+
# array to avoid passing copies of the array between calls.
28+
def mergeSort(a: List[int], b: List[int], l: int, r: int) -> None:
29+
# Base case, the array has only one element.
30+
if l == r:
31+
return
32+
mid = l + ((r - l) // 2)
33+
# Swap the function of each array in each call level.
34+
mergeSort(b, a, l, mid)
35+
mergeSort(b, a, mid + 1, r)
36+
merge(a, b, l, mid, r)
37+
38+
def merge(destination, source, l, mid, r):
39+
# The halves are sorted, merge them. Define i and j, two
40+
# read pointers that read the next unused element in each of
41+
# the sorted halves, and one insert pointer of the index at
42+
# which we want to insert the next value in the result.
43+
i, j, ins = l, mid + 1, l
44+
while i <= mid and j <= r:
45+
if source[j] < source[i]:
46+
destination[ins] = source[j]
47+
j += 1
48+
else:
49+
destination[ins] = source[i]
50+
i += 1
51+
ins += 1
52+
# Use up any remaining elements from either half.
53+
while i <= mid:
54+
destination[ins] = source[i]
55+
i += 1
56+
ins += 1
57+
while j <= r:
58+
destination[ins] = source[j]
59+
j += 1
60+
ins += 1
61+
62+
# A copy of the input array that we use to alternate between
63+
# sorted and unsorted sections.
64+
helper = nums[:]
65+
mergeSort(nums, helper, 0, len(nums) - 1)
66+
return nums
67+
68+
69+
def test():
70+
executors = [
71+
MergeSort,
72+
]
73+
tests = [
74+
[[0], [0]],
75+
[[1, 0], [0, 1]],
76+
[[5, 2, 1], [1, 2, 5]],
77+
[[5, 2, 3, 1], [1, 2, 3, 5]],
78+
[[5, 1, 1, 2, 0, 0], [0, 0, 1, 1, 2, 5]],
79+
]
80+
for executor in executors:
81+
start = timeit.default_timer()
82+
for _ in range(1):
83+
for col, t in enumerate(tests):
84+
sol = executor()
85+
result = sol.sortArray(t[0])
86+
exp = t[1]
87+
assert result == exp, (
88+
f"\033[93m» {result} <> {exp}\033[91m for"
89+
+ f" test {col} using \033[1m{executor.__name__}"
90+
)
91+
stop = timeit.default_timer()
92+
used = str(round(stop - start, 5))
93+
cols = "{0:20}{1:10}{2:10}"
94+
res = cols.format(executor.__name__, used, "seconds")
95+
print(f"\033[92m» {res}\033[0m")
96+
97+
98+
test()

leetcode/sort-an-array.rs

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// 912. Sort an Array
2+
// 🟠 Medium
3+
//
4+
// https://leetcode.com/problems/sort-an-array/
5+
//
6+
// Tags: Array - Divide and Conquer - Sorting - Heap (Priority Queue)
7+
// - Merge Sort - Bucket Sort - Radix Sort - Counting Sort
8+
9+
struct Solution;
10+
impl Solution {
11+
// Given the conditions of the problem description, merge sort seems to
12+
// be a good option, it won't use any extra memory and it guarantees
13+
// O(n*log(n)) time complexity.
14+
//
15+
// Time complexity: O(n*log(n))
16+
// Space complexity: O(n) - We use one copy of the input array to
17+
// alternately move elements from one to the other. The call stack will
18+
// be of height log(n), that is an extra O(log(n)).
19+
//
20+
// Runtime 46 ms Beats 90.32%
21+
// Memory 2.9 MB Beats 54.84%
22+
pub fn sort_array(nums: Vec<i32>) -> Vec<i32> {
23+
// Define an internal function that sorts a section of the input array, this
24+
// avoids passing copies of the array between calls.
25+
fn merge_sort(a: &mut Vec<i32>, b: &mut Vec<i32>, l: usize, r: usize) {
26+
if l == r {
27+
return;
28+
}
29+
let mid = l + ((r - l) / 2);
30+
// Swap the source and destination arrays in each call.
31+
merge_sort(b, a, l, mid);
32+
merge_sort(b, a, mid + 1, r);
33+
merge(a, b, l, mid, r);
34+
}
35+
36+
// A function that merges two sorted halves.
37+
fn merge(dest: &mut Vec<i32>, source: &mut Vec<i32>, l: usize, mid: usize, r: usize) {
38+
// The halves from l to mid and mid to r are sorted in the "source" array, we can
39+
// use elements from the sorted halves to update the elements in dest and end with
40+
// a completely sorted slice between l and r.
41+
let mut i = l;
42+
let mut j = mid + 1;
43+
let mut ins = l;
44+
while i <= mid && j <= r {
45+
if source[j] < source[i] {
46+
dest[ins] = source[j];
47+
j += 1;
48+
} else {
49+
dest[ins] = source[i];
50+
i += 1;
51+
}
52+
ins += 1;
53+
}
54+
// Use up any remaining elements from either half.
55+
while i <= mid {
56+
dest[ins] = source[i];
57+
i += 1;
58+
ins += 1;
59+
}
60+
while j <= r {
61+
dest[ins] = source[j];
62+
j += 1;
63+
ins += 1;
64+
}
65+
}
66+
// Use two mutable copies of the input array to avoid copying at each call level.
67+
let mut a = nums.clone();
68+
let mut b = nums.clone();
69+
merge_sort(&mut b, &mut a, 0, nums.len() - 1);
70+
b
71+
}
72+
73+
// TODO add bucket sort solution. In Rust it could be more performant.
74+
75+
// Use the built in sort function.
76+
//
77+
// Runtime 28 ms Beats 100%
78+
// Memory 2.8 MB Beats 67.74%
79+
pub fn _sort_array_built_in(nums: Vec<i32>) -> Vec<i32> {
80+
let mut n = nums.clone();
81+
n.sort_unstable();
82+
n
83+
}
84+
}
85+
86+
// Tests.
87+
fn main() {
88+
let tests = [
89+
(vec![0], vec![0]),
90+
(vec![1, 0], vec![0, 1]),
91+
(vec![5, 2, 1], vec![1, 2, 5]),
92+
(vec![5, 2, 3, 1], vec![1, 2, 3, 5]),
93+
(vec![5, 1, 1, 2, 0, 0], vec![0, 0, 1, 1, 2, 5]),
94+
];
95+
for test in tests {
96+
assert_eq!(Solution::sort_array(test.0), test.1);
97+
}
98+
println!("All tests passed!")
99+
}

0 commit comments

Comments
 (0)