Skip to content

Commit ee0744f

Browse files
committed
LC 135. Candy (Rust Greedy)
1 parent b35ec72 commit ee0744f

File tree

3 files changed

+124
-52
lines changed

3 files changed

+124
-52
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
124124
| [131. Palindrome Partitioning][lc131] | 🟠 Medium | [![python](res/py.png)][lc131py] |
125125
| [133. Clone Graph][lc133] | 🟠 Medium | [![python](res/py.png)][lc133py] |
126126
| [134. Gas Station][lc134] | 🟠 Medium | [![python](res/py.png)][lc134py] |
127-
| [135. Candy][lc135] | 🔴 Hard | [![python](res/py.png)][lc135py] |
127+
| [135. Candy][lc135] | 🔴 Hard | [![python](res/py.png)][lc135py] [![rust](res/rs.png)][lc135rs] |
128128
| [136. Single Number][lc136] | 🟢 Easy | [![python](res/py.png)][lc136py] |
129129
| [138. Copy List with Random Pointer][lc138] | 🟠 Medium | [![python](res/py.png)][lc138py] |
130130
| [139. Word Break][lc139] | 🟠 Medium | [![python](res/py.png)][lc139py] |
@@ -832,6 +832,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
832832
[lc134py]: leetcode/gas-station.py
833833
[lc135]: https://leetcode.com/problems/candy/
834834
[lc135py]: leetcode/candy.py
835+
[lc135rs]: leetcode/candy.rs
835836
[lc136]: https://leetcode.com/problems/single-number/
836837
[lc136py]: leetcode/single-number.py
837838
[lc138]: https://leetcode.com/problems/copy-list-with-random-pointer/

leetcode/candy.py

Lines changed: 71 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,55 @@
1+
# 135. Candy
2+
# 🔴 Hard
3+
#
14
# https://leetcode.com/problems/candy/
2-
5+
#
6+
# Tags: Array - Greedy
37

48
import timeit
59
from typing import List
610

711

8-
# Runtime: 263 ms, faster than 42.85% of Python3 online submissions for Candy.
9-
# Memory Usage: 16.9 MB, less than 37.74 % of Python3 online submissions for Candy.
12+
# Use an extra auxiliary vector of the same size as the input vector to
13+
# store the candy assigned to each child initialized to 1 for each child.
14+
# Iterate over the input array forward, for each child that has a higher
15+
# rating that the one to its left, give it one more candy that the one to
16+
# its left already has. Once we reach the end we traverse backwards, for
17+
# each child that has a higher rating than the one to its right, if its it
18+
# not already getting more candy than the one to its right is, we give it
19+
# the amount of candy that that one gets plus 1. We return the sum of
20+
# values in the assigned candy array.
21+
#
22+
# Time complexity: O(n) - We traverse over n elements twice.
23+
# Space complexity: O(n) - We store an extra vector of size n in memory.
24+
#
25+
# Runtime 141 ms Beats 73.84%
26+
# Memory 19.15 MB Beats 91.36%
27+
class TwoArrays:
28+
def candy(self, ratings):
29+
n = len(ratings)
30+
if n < 2:
31+
return n
32+
nums = [1] * n
33+
for i in range(1, n):
34+
if ratings[i] > ratings[i - 1]:
35+
nums[i] = nums[i - 1] + 1
36+
for i in range(n - 1, 0, -1):
37+
if ratings[i - 1] > ratings[i]:
38+
nums[i - 1] = max(nums[i] + 1, nums[i - 1])
39+
return sum(nums)
40+
41+
42+
# Similar solution that only uses one pass, keep track of how many
43+
# previous children's assignments depend on the current child, if it is
44+
# necessary to increase its assigned candy, increase it by one for it
45+
# and all previous children that had a higher rating.
46+
#
47+
# Time complexity: O(n) - We visit each of the n elements once and do
48+
# constant time work for each.
49+
# Space complexity: O(1) - We use constant extra memory.
50+
#
51+
# Runtime 136 ms Beats 88.46%
52+
# Memory 19.26 MB Beats 68.45%
1053
class DescendingSequence:
1154
def candy(self, ratings: List[int]) -> int:
1255
if len(ratings) < 2:
@@ -47,73 +90,50 @@ def candy(self, ratings: List[int]) -> int:
4790
return min_candy
4891

4992

50-
class TwoArrays:
51-
def candy(self, ratings):
52-
n = len(ratings)
53-
if n < 2:
54-
return n
55-
56-
# initial state: each kid gets one candy
57-
nums = [1] * n
58-
# kids on upwards curve get candies
59-
for i in range(1, n):
60-
if ratings[i] > ratings[i-1]:
61-
nums[i] = nums[i-1] + 1
62-
63-
# kids on downwards curve get candies
64-
# if a kid on both up/down curves, i.e. a peak or a valley
65-
# kid gets the maximum candies among the two.
66-
# total = 0
67-
for i in range(n-1, 0, -1):
68-
if ratings[i-1] > ratings[i]:
69-
nums[i-1] = max(nums[i]+1, nums[i-1])
70-
# total += nums[i]
71-
72-
# Locally it is quicker to return the sum
73-
# total += nums[0]
74-
# return total
75-
return sum(nums)
76-
77-
7893
def test():
79-
executor = [
80-
{'executor': DescendingSequence, 'title': 'DescendingSequence', },
81-
{'executor': TwoArrays, 'title': 'TwoArrays', },
94+
executors = [
95+
DescendingSequence,
96+
TwoArrays,
8297
]
8398
tests = [
99+
[[], 0],
100+
[[15], 1],
101+
[[1, 0, 2], 5],
102+
[[1, 2, 2], 4],
84103
[[0, 0, 0, 0], 4],
85104
[[0, 0, 0, -1], 5],
86105
[[7, 1, 0, -1], 10],
87-
[[7, 1, 0, -1, -1], 11],
88-
[[7, 1, 0, -1, -1, 1], 13],
89-
[[7, 1, 0, -1, -1, 1, 2], 16],
90-
[[7, 1, 0, -1, -1, 1, 2, 3], 20],
91-
[[7, 1, 0, -1, -1, 1, 2, 3, 3], 21],
92106
[[1, 2, 3, 4, 5], 15],
107+
[[7, 1, 0, -1, -1], 11],
93108
[[1, 2, 3, 4, 5, 4], 16],
109+
[[1, 5, 4, 3, 2, 1], 16],
110+
[[7, 1, 0, -1, -1, 1], 13],
94111
[[1, 2, 3, 4, 5, 4, 2], 18],
112+
[[7, 1, 0, -1, -1, 1, 2], 16],
95113
[[1, 2, 3, 4, 5, 4, 3, 2], 21],
114+
[[7, 1, 0, -1, -1, 1, 2, 3], 20],
96115
[[1, 2, 3, 4, 5, 4, 3, 2, 1], 25],
116+
[[7, 1, 0, -1, -1, 1, 2, 3, 3], 21],
97117
[[1, 2, 3, 4, 5, 4, 3, 2, 1, 0], 31],
98118
[[1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0], 32],
99119
[[1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0, 1, 2, 4], 41],
100-
[[1, 5, 4, 3, 2, 1], 16],
101-
[[1, 0, 2], 5],
102-
[[1, 2, 2], 4],
103-
[[], 0],
104-
[[15], 1],
105120
]
106-
for e in executor:
121+
for executor in executors:
107122
start = timeit.default_timer()
108-
for _ in range(int(float('1'))):
109-
for t in tests:
110-
sol = e['executor']()
123+
for _ in range(1):
124+
for col, t in enumerate(tests):
125+
sol = executor()
111126
result = sol.candy(t[0])
112-
expected = t[1]
113-
assert result == expected, f'{result} != {expected}'
127+
exp = t[1]
128+
assert result == exp, (
129+
f"\033[93m» {result} <> {exp}\033[91m for"
130+
+ f" test {col} using \033[1m{executor.__name__}"
131+
)
114132
stop = timeit.default_timer()
115133
used = str(round(stop - start, 5))
116-
print("{0:20}{1:10}{2:10}".format(e['title'], used, "seconds"))
134+
cols = "{0:20}{1:10}{2:10}"
135+
res = cols.format(executor.__name__, used, "seconds")
136+
print(f"\033[92m» {res}\033[0m")
117137

118138

119139
test()

leetcode/candy.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// 135. Candy
2+
// 🔴 Hard
3+
//
4+
// https://leetcode.com/problems/candy/
5+
//
6+
// Tags: Array - Greedy
7+
8+
struct Solution;
9+
impl Solution {
10+
/// Use an extra auxiliary vector of the same size as the input vector to
11+
/// store the candy assigned to each child initialized to 1 for each child.
12+
/// Iterate over the input array forward, for each child that has a higher
13+
/// rating that the one to its left, give it one more candy that the one to
14+
/// its left already has. Once we reach the end we traverse backwards, for
15+
/// each child that has a higher rating than the one to its right, if its it
16+
/// not already getting more candy than the one to its right is, we give it
17+
/// the amount of candy that that one gets plus 1. We return the sum of
18+
/// values in the assigned candy array.
19+
///
20+
/// Time complexity: O(n) - We traverse over n elements twice.
21+
/// Space complexity: O(n) - We store an extra vector of size n in memory.
22+
///
23+
/// Runtime 2 ms Beats 89.25%
24+
/// Memory 2.14 MB Beats 84.95%
25+
pub fn candy(ratings: Vec<i32>) -> i32 {
26+
let n = ratings.len();
27+
let mut assignments = vec![1; n];
28+
for i in 1..n {
29+
if ratings[i] > ratings[i - 1] {
30+
assignments[i] = assignments[i - 1] + 1;
31+
}
32+
}
33+
let mut res = assignments[n - 1];
34+
for i in (0..n - 1).rev() {
35+
if ratings[i] > ratings[i + 1] && assignments[i] <= assignments[i + 1] {
36+
assignments[i] = assignments[i + 1] + 1;
37+
}
38+
res += assignments[i];
39+
}
40+
res
41+
}
42+
}
43+
44+
// Tests.
45+
fn main() {
46+
let tests = [(vec![1, 0, 2], 5), (vec![1, 2, 2], 4)];
47+
for t in tests {
48+
assert_eq!(Solution::candy(t.0), t.1);
49+
}
50+
println!("\x1b[92m» All tests passed!\x1b[0m")
51+
}

0 commit comments

Comments
 (0)