Credits
Implement the Credits class, which should support the following operations: granting credits, subtracting credits, and getting the balance for a user.
The system must handle requests that arrive out of order. For example, a request to subtract credits may arrive before the request to add credits, even if the add request has a lower timestamp.
Some guidelines:
- Do not worry about memory or performance concerns. Write simple, working code.
- No fancy data structures are needed.
- Timestamps can be represented as ints for simplicity and are unique.
- Subtract from grants expiring soonest first.
Implement:
class Credits:
def create_grant(self, timestamp: int, grant_id: str, amount: int, expiration_timestamp: int) -> None:
pass
def subtract(self, timestamp: int, amount: int) -> None:
pass
def get_balance(self, timestamp: int) -> int | None:
pass
Example usage:
# basic subtraction
credits = Credits()
credits.subtract(30, amount=1)
credits.create_grant(10, grant_id="a", amount=1, expiration_timestamp=100)
assert credits.get_balance(10) == 1
assert credits.get_balance(30) == 0
assert credits.get_balance(20) == 1
# expiration
credits = Credits()
credits.subtract(30, amount=1)
credits.create_grant(10, grant_id="a", amount=2, expiration_timestamp=100)
assert credits.get_balance(10) == 2
assert credits.get_balance(20) == 2
assert credits.get_balance(30) == 1
assert credits.get_balance(100) == 0
# subtracting from soonest expiring grants first
credits = Credits()
credits.create_grant(10, grant_id="a", amount=3, expiration_timestamp=60)
credits.create_grant(20, grant_id="b", amount=2, expiration_timestamp=40)
credits.subtract(30, amount=1)
credits.subtract(50, amount=3)
assert credits.get_balance(10) == 3
assert credits.get_balance(20) == 5
assert credits.get_balance(30) == 4
assert credits.get_balance(40) == 3
assert credits.get_balance(50) == 0
# not enough credit
credits = Credits()
credits.create_grant(10, grant_id="a", amount=3, expiration_timestamp=60)
credits.subtract(20, amount=4)
credits.create_grant(40, grant_id="b", amount=10, expiration_timestamp=60)
assert credits.get_balance(10) == 3
assert credits.get_balance(20) is None
assert credits.get_balance(50) is None
这是一道典型的“带过期时间的积分账户”模拟题,核心是维护多个 credit grant,并支持按时间查询余额、按最早过期优先扣减,以及处理请求乱序到达的情况。实现时通常可以用一个按时间记录事件的简单结构,再在查询时把在该时间点之前生效的 grant 和扣减操作重新汇总,注意过期时间之后的 grant 不再计入余额。如果某次扣减会导致余额变成负数,后续在更晚时间点查询应返回 None,表示账户已经进入不可恢复的透支状态。