-
Notifications
You must be signed in to change notification settings - Fork 0
/
largest_number.py
120 lines (94 loc) · 5.07 KB
/
largest_number.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/env python3
# Largest Number
#
# https://leetcode.com/problems/largest-number/
#
# Given a list of non-negative integers nums, arrange them such that they form
# the largest number and return it.
#
# Since the result may be very large, so you need to return a string instead of
# an integer.
from functools import cmp_to_key
from typing import List
import re
def test():
"""
Run `pytest <this-file>`.
"""
def test_algo(algo):
assert algo(nums=[10, 2]) == "210"
assert algo(nums=[3, 30, 34, 5, 9]) == "9534330"
assert algo(nums=[432, 43243]) == "43243432"
# Test all different algorithms/implementations
solution = Solution()
for algo in [solution.custom_sort, solution.custom_sort_simplified, solution.custom_sort_optimized]:
test_algo(algo)
class Solution:
def custom_sort(self, nums: List[int]) -> str:
"""
Approach: Sort with custom comparator.
Idea: Sort the numbers descendingly, but comparisons need to include the greedy observation that in numbers AB and C, for AB to be larger than C if A=B, B also needs to be larger than C.
Time: O(m n log n): Given a list of size n, sorting takes O(n log n) comparisons. However, each comparison takes O(m), where m is the number of digits in the larger string,
Space: O(1): No additional memory is used.
Leetcode: 44 ms runtime, 16.74 MB memory
"""
def comparator(number_a: str, number_b: str) -> int:
def numbers_equal(): return 0
def number_a_smaller(): return -1
def number_a_larger(): return 1
(number_a_len, number_b_len) = (len(number_a), len(number_b))
min_len = min(number_a_len, number_b_len)
# Compare shared indices.
(number_a_shared, number_b_shared) = (int(number_a[0:min_len]), int(number_b[0:min_len]))
if number_a_shared < number_b_shared:
return number_a_smaller()
elif number_a_shared > number_b_shared:
return number_a_larger()
# If there is a longer number: its leftover digits must be larger than shorter number.
if number_a_len > number_b_len:
return comparator(number_a[min_len:], number_b)
elif number_a_len < number_b_len:
return comparator(number_a, number_b[min_len:])
return numbers_equal()
def trim_leading_zeros(number: str) -> str:
if (m := re.search(r'0*([0-9]+)', number)):
return m.group(1)
else:
raise Exception("unreachable: the regex will match any valid number")
largest_number = "".join(sorted(map(str, nums), key=cmp_to_key(comparator), reverse=True))
return trim_leading_zeros(largest_number)
def custom_sort_simplified(self, nums: List[int]) -> str:
"""
Approach: Sort with custom comparator.
Idea: Sort the numbers descendingly, but comparisons need to include the greedy observation that in numbers AB and C, for AB to be larger than C if A=B, B also needs to be larger than C. This can be enforced by checking if ABC is larger than CAB.
Time: O(n log n): Given a list of size n, sorting takes O(n log n) comparisons. Each comparison takes O(1).
Space: O(1): No additional memory is used.
Leetcode: 45 ms runtime, 16.37 MB memory
"""
def comparator(number_a: str, number_b: str) -> int:
return int(number_a + number_b) - int(number_b + number_a)
def trim_leading_zeros(number: str) -> str:
if (m := re.search(r'0*([0-9]+)', number)):
return m.group(1)
else:
raise Exception("unreachable: the regex will match any valid number")
largest_number = "".join(sorted(map(str, nums), key=cmp_to_key(comparator), reverse=True))
return trim_leading_zeros(largest_number)
def custom_sort_optimized(self, nums: List[int]) -> str:
"""
Approach: Sort with custom comparator.
Idea: Sort the numbers descendingly, but comparisons need to include the greedy observation that in numbers AB and C, for AB to be larger than C if A=B, B also needs to be larger than C. This can be enforced by checking if ABC is larger than CAB.
Time: O(n log n): Given a list of size n, sorting takes O(n log n) comparisons. Each comparison takes O(1).
Space: O(1): No additional memory is used.
Leetcode: 44 ms runtime, 16.50 MB memory
"""
def comparator(number_a: str, number_b: str) -> int:
return int(number_a + number_b) - int(number_b + number_a)
def trim_leading_zeros(number: str) -> str:
# The only way there is a leading zero is if the number only consists of zeros (since we sort descendingly).
if number[0] == "0":
return "0"
else:
return number
largest_number = "".join(sorted(map(str, nums), key=cmp_to_key(comparator), reverse=True))
return trim_leading_zeros(largest_number)