Design Tic-Tac-Toe and QPS data structures
DatabricksSoftware Engineer · OnsiteLast updated
This question evaluates data-structure and API design skills, covering state management and efficient update/query algorithms for a generalized Tic-Tac-Toe with uniform-random move selection, as well as time-series aggregation and streaming-log techniques for computing QPS from non-decreasing timestamps.
You are given two independent coding problems that focus on data structure and API design.
Problem 1: Generalized Tic-Tac-Toe Game with Simple AI
Design a Tic-Tac-Toe game that supports a generalized board size and a customizable win condition.
Implement a class TicTacToe with the following behavior:
- The game is played by two players, labeled
1and2. - The board has
rowsrows andcolscolumns. - A player wins if they have K of their marks in a line, where a line can be:
- A horizontal line
- A vertical line
- A main diagonal (top-left to bottom-right)
- An anti-diagonal (top-right to bottom-left)
API
TicTacToe(int rows, int cols, int K)
- Initializes the game board with the given dimensions and win condition
K. - All cells are initially empty.
int move(int row, int col, int player)
row,colare 0-indexed coordinates on the board.playeris either1or2.- Places the player's mark at
(row, col). - Returns:
0if no one has won after this move,1if player 1 wins as a result of this move,2if player 2 wins as a result of this move,-1if the move is invalid (out of bounds or the cell is already occupied).
You should design the data structures so that each move call is efficient even when the board is large.
Constraints (you may assume):
1 <= rows, cols <= 10001 <= K <= max(rows, cols)- Total number of moves ≤
rows * cols.
Follow-up: Random-Move AI
Extend the design with a very simple AI that chooses a random legal move for a given player.
You are given a helper function:
int randInt(int low, int high)
which returns a uniform random integer in the inclusive range [low, high].
Add a method:
pair<int, int> getNextMove(int player)
- Returns a pair
(row, col)corresponding to an empty cell whereplayercan play next. - The AI should pick uniformly at random among all currently empty cells.
- If there is no legal move (the board is full or the game is already over), you may return a special value, such as
(-1, -1).
Design this so that getNextMove runs efficiently, even on a large board with many moves already played.
Problem 2: Query QPS from KV Store Request Logs
A key-value (KV) store receives a large number of requests. Each request is logged with a timestamp (in seconds). You want to support efficient queries about the queries per second (QPS) over arbitrary time ranges.
You are given an initially empty log. Implement a data structure that supports the following operations:
void record(long timestamp)
- Called once for each request handled by the KV store.
timestampis an integer representing seconds since some fixed epoch.- Timestamps are not guaranteed to be contiguous but you may assume they are non-decreasing (each new call has
timestamp >=previoustimestamp).
double getQPS(long startTime, long endTime)
- Returns the average QPS in the inclusive time interval
[startTime, endTime]. - Formally, if
countrequests have timestampstsuch thatstartTime <= t <= endTime, then:
QPS = count / (endTime - startTime + 1)
- You should support many
getQPSqueries efficiently after recording a large volume of data.
Constraints (you may assume):
- Up to
N = 10^6calls torecord. - Up to
Q = 10^6calls togetQPS. - Timestamps fit in a 64-bit signed integer.
Follow-up 1: Efficient Arbitrary-Time Queries
The naive implementation might scan all timestamps within [startTime, endTime] for each query, which is too slow when Q is large.
Design and describe a more efficient approach that:
- Uses reasonable additional memory.
- Supports
getQPS(startTime, endTime)in sublinear time inN(for example,O(log N)per query).
Your design should be implementable using common data structures (arrays, lists, hash maps, trees, etc.).
Follow-up 2: Trade Accuracy for Less Memory
Now suppose memory is tight and you are allowed to sacrifice precision to save space. Instead of storing every individual timestamp, you can approximate the QPS.
Design a modified scheme that:
- Uses significantly less memory than the exact solution.
- Returns an approximate QPS for any
[startTime, endTime]query. - Clearly states what is being approximated (for example, grouping multiple seconds into a time range / bucket) and what kind of error might be introduced.
You should:
- Describe the data you would store (e.g., buckets or ranges instead of individual timestamps).
- Explain how
getQPSwill be computed from this aggregated data. - Reason about the trade-off between memory usage, accuracy, and query performance.
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.