Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Move from brute-force thinking to an efficient approach using hash map strategy.
You are asked to design an auction system that manages bids from multiple users in real time.
Each bid is associated with a userId, an itemId, and a bidAmount.
Implement the AuctionSystem class:
AuctionSystem(): Initializes the AuctionSystem object.void addBid(int userId, int itemId, int bidAmount): Adds a new bid for itemId by userId with bidAmount. If the same userId already has a bid on itemId, replace it with the new bidAmount.void updateBid(int userId, int itemId, int newAmount): Updates the existing bid of userId for itemId to newAmount. It is guaranteed that this bid exists.void removeBid(int userId, int itemId): Removes the bid of userId for itemId. It is guaranteed that this bid exists.int getHighestBidder(int itemId): Returns the userId of the highest bidder for itemId. If multiple users have the same highest bidAmount, return the user with the highest userId. If no bids exist for the item, return -1.Example 1:
Input:
["AuctionSystem", "addBid", "addBid", "getHighestBidder", "updateBid", "getHighestBidder", "removeBid", "getHighestBidder", "getHighestBidder"]
[[], [1, 7, 5], [2, 7, 6], [7], [1, 7, 8], [7], [2, 7], [7], [3]]
Output:
[null, null, null, 2, null, 1, null, 1, -1]
Explanation
AuctionSystem auctionSystem = new AuctionSystem(); // Initialize the Auction systemConstraints:
1 <= userId, itemId <= 5 * 1041 <= bidAmount, newAmount <= 1095 * 104 total calls to addBid, updateBid, removeBid, and getHighestBidder.updateBid and removeBid, the bid from the given userId for the given itemId will be valid.Problem summary: You are asked to design an auction system that manages bids from multiple users in real time. Each bid is associated with a userId, an itemId, and a bidAmount. Implement the AuctionSystem class: AuctionSystem(): Initializes the AuctionSystem object. void addBid(int userId, int itemId, int bidAmount): Adds a new bid for itemId by userId with bidAmount. If the same userId already has a bid on itemId, replace it with the new bidAmount. void updateBid(int userId, int itemId, int newAmount): Updates the existing bid of userId for itemId to newAmount. It is guaranteed that this bid exists. void removeBid(int userId, int itemId): Removes the bid of userId for itemId. It is guaranteed that this bid exists. int getHighestBidder(int itemId): Returns the userId of the highest bidder for itemId. If multiple users have the same highest bidAmount, return the user with the highest userId. If no
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Design · Segment Tree
["AuctionSystem","addBid","addBid","getHighestBidder","updateBid","getHighestBidder","removeBid","getHighestBidder","getHighestBidder"] [[],[1,7,5],[2,7,6],[7],[1,7,8],[7],[2,7],[7],[3]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3815: Design Auction System
class AuctionSystem {
private final Map<Integer, TreeSet<int[]>> items = new HashMap<>();
private final Map<Integer, Map<Integer, Integer>> users = new HashMap<>();
public AuctionSystem() {
}
public void addBid(int userId, int itemId, int bidAmount) {
users.computeIfAbsent(userId, k -> new HashMap<>());
if (users.get(userId).containsKey(itemId)) {
removeBid(userId, itemId);
}
users.get(userId).put(itemId, bidAmount);
items.computeIfAbsent(itemId,
k
-> new TreeSet<>(
(a, b)
-> a[0] != b[0] ? Integer.compare(a[0], b[0]) : Integer.compare(a[1], b[1])));
items.get(itemId).add(new int[] {bidAmount, userId});
}
public void updateBid(int userId, int itemId, int newAmount) {
int oldAmount = users.get(userId).get(itemId);
TreeSet<int[]> set = items.get(itemId);
set.remove(new int[] {oldAmount, userId});
set.add(new int[] {newAmount, userId});
users.get(userId).put(itemId, newAmount);
}
public void removeBid(int userId, int itemId) {
int oldAmount = users.get(userId).get(itemId);
TreeSet<int[]> set = items.get(itemId);
set.remove(new int[] {oldAmount, userId});
users.get(userId).remove(itemId);
}
public int getHighestBidder(int itemId) {
TreeSet<int[]> set = items.get(itemId);
if (set == null || set.isEmpty()) {
return -1;
}
return set.last()[1];
}
}
/**
* Your AuctionSystem object will be instantiated and called as such:
* AuctionSystem obj = new AuctionSystem();
* obj.addBid(userId,itemId,bidAmount);
* obj.updateBid(userId,itemId,newAmount);
* obj.removeBid(userId,itemId);
* int param_4 = obj.getHighestBidder(itemId);
*/
// Accepted solution for LeetCode #3815: Design Auction System
package main
import "container/heap"
// https://space.bilibili.com/206214
type pair struct{ userId, itemId int }
type AuctionSystem struct {
itemH map[int]*hp // itemId -> [(bidAmount, userId)]
amount map[pair]int // (userId, itemId) -> bidAmount
}
func Constructor() AuctionSystem {
return AuctionSystem{map[int]*hp{}, map[pair]int{}}
}
func (a AuctionSystem) AddBid(userId, itemId, bidAmount int) {
a.amount[pair{userId, itemId}] = bidAmount
if a.itemH[itemId] == nil {
a.itemH[itemId] = &hp{}
}
heap.Push(a.itemH[itemId], hpPair{bidAmount, userId})
}
func (a AuctionSystem) UpdateBid(userId, itemId, newAmount int) {
a.AddBid(userId, itemId, newAmount)
// 堆中重复的元素在 GetHighestBidder 中删除(懒更新)
}
func (a AuctionSystem) RemoveBid(userId, itemId int) {
delete(a.amount, pair{userId, itemId})
// 堆中元素在 GetHighestBidder 中删除(懒删除)
}
func (a AuctionSystem) GetHighestBidder(itemId int) (ans int) {
h := a.itemH[itemId]
if h == nil {
return -1
}
for h.Len() > 0 {
if (*h)[0].bidAmount == a.amount[pair{(*h)[0].userId, itemId}] {
return (*h)[0].userId
}
// 货不对板,堆顶出价与实际不符
heap.Pop(h)
}
return -1
}
type hpPair struct{ bidAmount, userId int }
type hp []hpPair
func (h hp) Len() int { return len(h) }
func (h hp) Less(i, j int) bool {
a, b := h[i], h[j]
return a.bidAmount > b.bidAmount || a.bidAmount == b.bidAmount && a.userId > b.userId
}
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp) Push(v any) { *h = append(*h, v.(hpPair)) }
func (h *hp) Pop() any { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }
# Accepted solution for LeetCode #3815: Design Auction System
class AuctionSystem:
def __init__(self):
self.items = defaultdict(SortedList)
self.users = {}
def addBid(self, userId: int, itemId: int, bidAmount: int) -> None:
if userId not in self.users:
self.users[userId] = {}
if itemId in self.users[userId]:
self.removeBid(userId, itemId)
self.users[userId][itemId] = bidAmount
self.items[itemId].add((bidAmount, userId))
def updateBid(self, userId: int, itemId: int, newAmount: int) -> None:
oldAmount = self.users[userId][itemId]
self.items[itemId].remove((oldAmount, userId))
self.items[itemId].add((newAmount, userId))
self.users[userId][itemId] = newAmount
def removeBid(self, userId: int, itemId: int) -> None:
oldAmount = self.users[userId][itemId]
self.items[itemId].remove((oldAmount, userId))
self.users[userId].pop(itemId)
def getHighestBidder(self, itemId: int) -> int:
ls = self.items[itemId]
return -1 if not ls else ls[-1][1]
# Your AuctionSystem object will be instantiated and called as such:
# obj = AuctionSystem()
# obj.addBid(userId,itemId,bidAmount)
# obj.updateBid(userId,itemId,newAmount)
# obj.removeBid(userId,itemId)
# param_4 = obj.getHighestBidder(itemId)
// Accepted solution for LeetCode #3815: Design Auction System
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3815: Design Auction System
// class AuctionSystem {
// private final Map<Integer, TreeSet<int[]>> items = new HashMap<>();
// private final Map<Integer, Map<Integer, Integer>> users = new HashMap<>();
//
// public AuctionSystem() {
// }
//
// public void addBid(int userId, int itemId, int bidAmount) {
// users.computeIfAbsent(userId, k -> new HashMap<>());
//
// if (users.get(userId).containsKey(itemId)) {
// removeBid(userId, itemId);
// }
//
// users.get(userId).put(itemId, bidAmount);
//
// items.computeIfAbsent(itemId,
// k
// -> new TreeSet<>(
// (a, b)
// -> a[0] != b[0] ? Integer.compare(a[0], b[0]) : Integer.compare(a[1], b[1])));
//
// items.get(itemId).add(new int[] {bidAmount, userId});
// }
//
// public void updateBid(int userId, int itemId, int newAmount) {
// int oldAmount = users.get(userId).get(itemId);
// TreeSet<int[]> set = items.get(itemId);
//
// set.remove(new int[] {oldAmount, userId});
// set.add(new int[] {newAmount, userId});
//
// users.get(userId).put(itemId, newAmount);
// }
//
// public void removeBid(int userId, int itemId) {
// int oldAmount = users.get(userId).get(itemId);
// TreeSet<int[]> set = items.get(itemId);
//
// set.remove(new int[] {oldAmount, userId});
// users.get(userId).remove(itemId);
// }
//
// public int getHighestBidder(int itemId) {
// TreeSet<int[]> set = items.get(itemId);
// if (set == null || set.isEmpty()) {
// return -1;
// }
// return set.last()[1];
// }
// }
//
// /**
// * Your AuctionSystem object will be instantiated and called as such:
// * AuctionSystem obj = new AuctionSystem();
// * obj.addBid(userId,itemId,bidAmount);
// * obj.updateBid(userId,itemId,newAmount);
// * obj.removeBid(userId,itemId);
// * int param_4 = obj.getHighestBidder(itemId);
// */
// Accepted solution for LeetCode #3815: Design Auction System
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #3815: Design Auction System
// class AuctionSystem {
// private final Map<Integer, TreeSet<int[]>> items = new HashMap<>();
// private final Map<Integer, Map<Integer, Integer>> users = new HashMap<>();
//
// public AuctionSystem() {
// }
//
// public void addBid(int userId, int itemId, int bidAmount) {
// users.computeIfAbsent(userId, k -> new HashMap<>());
//
// if (users.get(userId).containsKey(itemId)) {
// removeBid(userId, itemId);
// }
//
// users.get(userId).put(itemId, bidAmount);
//
// items.computeIfAbsent(itemId,
// k
// -> new TreeSet<>(
// (a, b)
// -> a[0] != b[0] ? Integer.compare(a[0], b[0]) : Integer.compare(a[1], b[1])));
//
// items.get(itemId).add(new int[] {bidAmount, userId});
// }
//
// public void updateBid(int userId, int itemId, int newAmount) {
// int oldAmount = users.get(userId).get(itemId);
// TreeSet<int[]> set = items.get(itemId);
//
// set.remove(new int[] {oldAmount, userId});
// set.add(new int[] {newAmount, userId});
//
// users.get(userId).put(itemId, newAmount);
// }
//
// public void removeBid(int userId, int itemId) {
// int oldAmount = users.get(userId).get(itemId);
// TreeSet<int[]> set = items.get(itemId);
//
// set.remove(new int[] {oldAmount, userId});
// users.get(userId).remove(itemId);
// }
//
// public int getHighestBidder(int itemId) {
// TreeSet<int[]> set = items.get(itemId);
// if (set == null || set.isEmpty()) {
// return -1;
// }
// return set.last()[1];
// }
// }
//
// /**
// * Your AuctionSystem object will be instantiated and called as such:
// * AuctionSystem obj = new AuctionSystem();
// * obj.addBid(userId,itemId,bidAmount);
// * obj.updateBid(userId,itemId,newAmount);
// * obj.removeBid(userId,itemId);
// * int param_4 = obj.getHighestBidder(itemId);
// */
Use this to step through a reusable interview workflow for this problem.
Use a simple list or array for storage. Each operation (get, put, remove) requires a linear scan to find the target element — O(n) per operation. Space is O(n) to store the data. The linear search makes this impractical for frequent operations.
Design problems target O(1) amortized per operation by combining data structures (hash map + doubly-linked list for LRU, stack + min-tracking for MinStack). Space is always at least O(n) to store the data. The challenge is achieving constant-time operations through clever structure composition.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.