Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
Design a data structure that can efficiently manage data packets in a network router. Each data packet consists of the following attributes:
source: A unique identifier for the machine that generated the packet.destination: A unique identifier for the target machine.timestamp: The time at which the packet arrived at the router.Implement the Router class:
Router(int memoryLimit): Initializes the Router object with a fixed memory limit.
memoryLimit is the maximum number of packets the router can store at any given time.bool addPacket(int source, int destination, int timestamp): Adds a packet with the given attributes to the router.
source, destination, and timestamp already exists in the router.true if the packet is successfully added (i.e., it is not a duplicate); otherwise return false.int[] forwardPacket(): Forwards the next packet in FIFO (First In First Out) order.
[source, destination, timestamp].int getCount(int destination, int startTime, int endTime):
[startTime, endTime].Note that queries for addPacket will be made in non-decreasing order of timestamp.
Example 1:
Input:
["Router", "addPacket", "addPacket", "addPacket", "addPacket", "addPacket", "forwardPacket", "addPacket", "getCount"]
[[3], [1, 4, 90], [2, 5, 90], [1, 4, 90], [3, 5, 95], [4, 5, 105], [], [5, 2, 110], [5, 100, 110]]
Output:
[null, true, true, false, true, true, [2, 5, 90], true, 1]
Explanation
Router router = new Router(3); // Initialize Router with memoryLimit of 3.[1, 4, 90] is removed as number of packets exceeds memoryLimit. Return True.[2, 5, 90] and remove it from router.[100, 110] is [4, 5, 105]. Return 1.Example 2:
Input:
["Router", "addPacket", "forwardPacket", "forwardPacket"]
[[2], [7, 4, 90], [], []]
Output:
[null, true, [7, 4, 90], []]
Explanation
Router router = new Router(2); // InitializeRouter with memoryLimit of 2.[7, 4, 90].[].Constraints:
2 <= memoryLimit <= 1051 <= source, destination <= 2 * 1051 <= timestamp <= 1091 <= startTime <= endTime <= 109105 calls will be made to addPacket, forwardPacket, and getCount methods altogether.addPacket will be made in non-decreasing order of timestamp.Problem summary: Design a data structure that can efficiently manage data packets in a network router. Each data packet consists of the following attributes: source: A unique identifier for the machine that generated the packet. destination: A unique identifier for the target machine. timestamp: The time at which the packet arrived at the router. Implement the Router class: Router(int memoryLimit): Initializes the Router object with a fixed memory limit. memoryLimit is the maximum number of packets the router can store at any given time. If adding a new packet would exceed this limit, the oldest packet must be removed to free up space. bool addPacket(int source, int destination, int timestamp): Adds a packet with the given attributes to the router. A packet is considered a duplicate if another packet with the same source, destination, and timestamp already exists in the router. Return true if the packet
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Binary Search · Design · Segment Tree
["Router","addPacket","addPacket","addPacket","addPacket","addPacket","forwardPacket","addPacket","getCount"] [[3],[1,4,90],[2,5,90],[1,4,90],[3,5,95],[4,5,105],[],[5,2,110],[5,100,110]]
["Router","addPacket","forwardPacket","forwardPacket"] [[2],[7,4,90],[],[]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3508: Implement Router
class Router {
private int lim;
private Set<Long> vis = new HashSet<>();
private Deque<int[]> q = new ArrayDeque<>();
private Map<Integer, Integer> idx = new HashMap<>();
private Map<Integer, List<Integer>> d = new HashMap<>();
public Router(int memoryLimit) {
this.lim = memoryLimit;
}
public boolean addPacket(int source, int destination, int timestamp) {
long x = f(source, destination, timestamp);
if (vis.contains(x)) {
return false;
}
vis.add(x);
if (q.size() >= lim) {
forwardPacket();
}
q.offer(new int[] {source, destination, timestamp});
d.computeIfAbsent(destination, k -> new ArrayList<>()).add(timestamp);
return true;
}
public int[] forwardPacket() {
if (q.isEmpty()) {
return new int[] {};
}
int[] packet = q.poll();
int s = packet[0], d_ = packet[1], t = packet[2];
vis.remove(f(s, d_, t));
idx.merge(d_, 1, Integer::sum);
return new int[] {s, d_, t};
}
private long f(int a, int b, int c) {
return ((long) a << 46) | ((long) b << 29) | (long) c;
}
public int getCount(int destination, int startTime, int endTime) {
List<Integer> ls = d.getOrDefault(destination, List.of());
int k = idx.getOrDefault(destination, 0);
int i = lowerBound(ls, startTime, k);
int j = lowerBound(ls, endTime + 1, k);
return j - i;
}
private int lowerBound(List<Integer> list, int target, int fromIndex) {
int l = fromIndex, r = list.size();
while (l < r) {
int m = (l + r) >>> 1;
if (list.get(m) < target) {
l = m + 1;
} else {
r = m;
}
}
return l;
}
}
/**
* Your Router object will be instantiated and called as such:
* Router obj = new Router(memoryLimit);
* boolean param_1 = obj.addPacket(source,destination,timestamp);
* int[] param_2 = obj.forwardPacket();
* int param_3 = obj.getCount(destination,startTime,endTime);
*/
// Accepted solution for LeetCode #3508: Implement Router
type Router struct {
lim int
vis map[int64]struct{}
q [][3]int
idx map[int]int
d map[int][]int
}
func Constructor(memoryLimit int) Router {
return Router{
lim: memoryLimit,
vis: make(map[int64]struct{}),
q: make([][3]int, 0),
idx: make(map[int]int),
d: make(map[int][]int),
}
}
func (this *Router) f(a, b, c int) int64 {
return int64(a)<<46 | int64(b)<<29 | int64(c)
}
func (this *Router) AddPacket(source int, destination int, timestamp int) bool {
x := this.f(source, destination, timestamp)
if _, ok := this.vis[x]; ok {
return false
}
this.vis[x] = struct{}{}
if len(this.q) >= this.lim {
this.ForwardPacket()
}
this.q = append(this.q, [3]int{source, destination, timestamp})
this.d[destination] = append(this.d[destination], timestamp)
return true
}
func (this *Router) ForwardPacket() []int {
if len(this.q) == 0 {
return []int{}
}
packet := this.q[0]
this.q = this.q[1:]
s, d, t := packet[0], packet[1], packet[2]
delete(this.vis, this.f(s, d, t))
this.idx[d]++
return []int{s, d, t}
}
func (this *Router) GetCount(destination int, startTime int, endTime int) int {
ls := this.d[destination]
k := this.idx[destination]
i := sort.Search(len(ls)-k, func(i int) bool { return ls[i+k] >= startTime }) + k
j := sort.Search(len(ls)-k, func(i int) bool { return ls[i+k] >= endTime+1 }) + k
return j - i
}
/**
* Your Router object will be instantiated and called as such:
* obj := Constructor(memoryLimit)
* param_1 := obj.AddPacket(source,destination,timestamp)
* param_2 := obj.ForwardPacket()
* param_3 := obj.GetCount(destination,startTime,endTime)
*/
# Accepted solution for LeetCode #3508: Implement Router
class Router:
def __init__(self, memoryLimit: int):
self.lim = memoryLimit
self.vis = set()
self.q = deque()
self.idx = defaultdict(int)
self.d = defaultdict(list)
def addPacket(self, source: int, destination: int, timestamp: int) -> bool:
x = self.f(source, destination, timestamp)
if x in self.vis:
return False
self.vis.add(x)
if len(self.q) >= self.lim:
self.forwardPacket()
self.q.append((source, destination, timestamp))
self.d[destination].append(timestamp)
return True
def forwardPacket(self) -> List[int]:
if not self.q:
return []
s, d, t = self.q.popleft()
self.vis.remove(self.f(s, d, t))
self.idx[d] += 1
return [s, d, t]
def f(self, a: int, b: int, c: int) -> int:
return a << 46 | b << 29 | c
def getCount(self, destination: int, startTime: int, endTime: int) -> int:
ls = self.d[destination]
k = self.idx[destination]
i = bisect_left(ls, startTime, k)
j = bisect_left(ls, endTime + 1, k)
return j - i
# Your Router object will be instantiated and called as such:
# obj = Router(memoryLimit)
# param_1 = obj.addPacket(source,destination,timestamp)
# param_2 = obj.forwardPacket()
# param_3 = obj.getCount(destination,startTime,endTime)
// Accepted solution for LeetCode #3508: Implement Router
use std::collections::{HashSet, HashMap, VecDeque};
struct Router {
lim: usize,
vis: HashSet<i64>,
q: VecDeque<(i32, i32, i32)>,
idx: HashMap<i32, usize>,
d: HashMap<i32, Vec<i32>>,
}
impl Router {
fn new(memory_limit: i32) -> Self {
Router {
lim: memory_limit as usize,
vis: HashSet::new(),
q: VecDeque::new(),
idx: HashMap::new(),
d: HashMap::new(),
}
}
fn f(a: i32, b: i32, c: i32) -> i64 {
((a as i64) << 46) | ((b as i64) << 29) | (c as i64)
}
fn add_packet(&mut self, source: i32, destination: i32, timestamp: i32) -> bool {
let x = Self::f(source, destination, timestamp);
if self.vis.contains(&x) {
return false;
}
self.vis.insert(x);
if self.q.len() >= self.lim {
self.forward_packet();
}
self.q.push_back((source, destination, timestamp));
self.d.entry(destination).or_default().push(timestamp);
true
}
fn forward_packet(&mut self) -> Vec<i32> {
if let Some((s, d, t)) = self.q.pop_front() {
self.vis.remove(&Self::f(s, d, t));
*self.idx.entry(d).or_insert(0) += 1;
vec![s, d, t]
} else {
vec![]
}
}
fn get_count(&self, destination: i32, start_time: i32, end_time: i32) -> i32 {
let ls = self.d.get(&destination);
if ls.is_none() {
return 0;
}
let ls = ls.unwrap();
let k = *self.idx.get(&destination).unwrap_or(&0);
let i = k + ls[k..].partition_point(|&x| x < start_time);
let j = k + ls[k..].partition_point(|&x| x < end_time + 1);
(j - i) as i32
}
}
// Accepted solution for LeetCode #3508: Implement Router
class Router {
private lim: number;
private vis: Set<number>;
private q: [number, number, number][];
private idx: Map<number, number>;
private d: Map<number, number[]>;
constructor(memoryLimit: number) {
this.lim = memoryLimit;
this.vis = new Set();
this.q = [];
this.idx = new Map();
this.d = new Map();
}
private f(a: number, b: number, c: number): number {
return ((BigInt(a) << 46n) | (BigInt(b) << 29n) | BigInt(c)) as unknown as number;
}
addPacket(source: number, destination: number, timestamp: number): boolean {
const x = this.f(source, destination, timestamp);
if (this.vis.has(x)) {
return false;
}
this.vis.add(x);
if (this.q.length >= this.lim) {
this.forwardPacket();
}
this.q.push([source, destination, timestamp]);
if (!this.d.has(destination)) {
this.d.set(destination, []);
}
this.d.get(destination)!.push(timestamp);
return true;
}
forwardPacket(): number[] {
if (this.q.length === 0) {
return [];
}
const [s, d, t] = this.q.shift()!;
this.vis.delete(this.f(s, d, t));
this.idx.set(d, (this.idx.get(d) ?? 0) + 1);
return [s, d, t];
}
getCount(destination: number, startTime: number, endTime: number): number {
const ls = this.d.get(destination) ?? [];
const k = this.idx.get(destination) ?? 0;
const i = this.lowerBound(ls, startTime, k);
const j = this.lowerBound(ls, endTime + 1, k);
return j - i;
}
private lowerBound(arr: number[], target: number, from: number): number {
let l = from,
r = arr.length;
while (l < r) {
const m = Math.floor((l + r) / 2);
if (arr[m] < target) {
l = m + 1;
} else {
r = m;
}
}
return l;
}
}
/**
* Your Router object will be instantiated and called as such:
* var obj = new Router(memoryLimit)
* var param_1 = obj.addPacket(source,destination,timestamp)
* var param_2 = obj.forwardPacket()
* var param_3 = obj.getCount(destination,startTime,endTime)
*/
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
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.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.