Implement Article Vote Tracking
RipplingSoftware Engineer · Technical ScreenLast updated
Design an in-memory article vote tracking system that manages user upvotes/downvotes, detects vote flips to maintain an LRU-style flip history per user, and provides top-k article rankings by net score.
Problem Statement
Design an article vote tracking system that manages user upvotes and downvotes, tracks voting history, detects vote "flips", and ranks articles based on popularity.
Implement the ArticleVoteTracker class:
ArticleVoteTracker(): Initializes the tracker.vote(user_id: int, article_id: int, vote_type: int) -> None:- Records a vote for
article_idbyuser_id.vote_typeis either1(upvote) or-1(downvote). - If the user has not voted on this article before, record the vote and adjust the article's net score.
- If the user votes the same as their existing vote on this article, do nothing.
- If the user votes opposite to their previous vote (i.e., changing from
1to-1or-1to1), this is considered a flip. Update the article's net score accordingly and record this article in the user's flip history as the most recently flipped article. If the article was already in the user's flip history, move it to the most recent position (maintaining unique article IDs).
- Records a vote for
get_score(article_id: int) -> int:- Returns the current net score (total upvotes minus total downvotes) of
article_id. If the article has received no votes, return0.
- Returns the current net score (total upvotes minus total downvotes) of
get_user_flips(user_id: int, limit: int) -> list[int]:- Returns a list of up to
limitunique article IDs thatuser_idhas flipped, ordered from most recently flipped to least recently flipped.
- Returns a list of up to
get_top_articles(k: int) -> list[int]:- Returns the top
karticle IDs with the highest net scores among all articles that have ever received at least one vote. If scores are tied, prioritize the smallerarticle_id. If fewer thankarticles exist, return all of them in ranked order.
- Returns the top
Example
tracker = ArticleVoteTracker()
# User 1 upvotes article 101, User 2 downvotes article 101
tracker.vote(user_id=1, article_id=101, vote_type=1) # score: 1
tracker.vote(user_id=2, article_id=101, vote_type=-1) # score: 0
assert tracker.get_score(101) == 0
# User 1 upvotes article 102
tracker.vote(user_id=1, article_id=102, vote_type=1) # score: 1
# User 1 flips their vote on article 101 from +1 to -1
tracker.vote(user_id=1, article_id=101, vote_type=-1) # score: -2 (User 1 is -1, User 2 is -1)
assert tracker.get_score(101) == -2
assert tracker.get_user_flips(1, 5) == [101]
# User 1 flips article 102 from +1 to -1
tracker.vote(user_id=1, article_id=102, vote_type=-1)
assert tracker.get_user_flips(1, 5) == [102, 101]
# Top articles: article 102 has score -1, article 101 has score -2
assert tracker.get_top_articles(2) == [102, 101]
Constraints
1 <= user_id, article_id <= 10^9vote_type in (1, -1)1 <= limit, k <= 1000- At most
2 * 10^5total calls across all methods. get_top_articlesshould run efficiently even if the total number of articles is large.
Hints
Hint 1
To track each user's flip history with uniqueness and recency, an OrderedDict or a doubly linked list combined with a hash map per user works in O(1) time.
Hint 2
A flip from +1 to -1 changes the net score by -2, and from -1 to +1 changes it by +2.
Hint 3
For dynamic ranking, you can use a min-heap of size k over all tracked articles on query, or maintain article scores in a balanced BST / bucket structure to make top-k queries faster.
Follow-up questions
- How would you optimize `get_top_articles` if calls to it are very frequent compared to `vote` calls?
- How would you support vote cancellations (i.e., a user removes their vote entirely), and how would that affect flip history?
- How would you scale this design across multiple distributed worker nodes while maintaining consistency of scores and user flip histories?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.