Back to all solutions

#3508 - Implement Router

Problem Description

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 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.

  • Remove the packet from storage.
  • Return the packet as an array [source, destination, timestamp].
  • If there are no packets to forward, return an empty array.

int getCount(int destination, int startTime, int endTime):

  • Returns the number of packets currently stored in the router (i.e., not yet forwarded) that have the specified destination and have timestamps in the inclusive range [startTime, endTime].

Note that queries for addPacket will be made in increasing order of timestamp.

Solution

/**
 * @param {number} memoryLimit
 */
var Router = function(memoryLimit) {
  this.memoryCapacity = memoryLimit;
  this.packetQueue = [];
  this.packetSet = new Set();
  this.destinationTimestamps = new Map();
  this.removedPacketIndex = new Map();
};

/**
 * @param {number} source
 * @param {number} destination
 * @param {number} timestamp
 * @return {boolean}
 */
Router.prototype.addPacket = function(source, destination, timestamp) {
  const packetKey = `${source}-${destination}-${timestamp}`;

  if (this.packetSet.has(packetKey)) {
    return false;
  }

  if (this.packetQueue.length === this.memoryCapacity) {
    const [oldSource, oldDestination, oldTimestamp] = this.packetQueue.shift();
    const oldPacketKey = `${oldSource}-${oldDestination}-${oldTimestamp}`;
    this.packetSet.delete(oldPacketKey);
    this.removedPacketIndex.set(
      oldDestination,
      (this.removedPacketIndex.get(oldDestination) || 0) + 1,
    );
  }

  this.packetQueue.push([source, destination, timestamp]);
  this.packetSet.add(packetKey);

  if (!this.destinationTimestamps.has(destination)) {
    this.destinationTimestamps.set(destination, []);
  }
  this.destinationTimestamps.get(destination).push(timestamp);

  return true;
};

/**
 * @return {number[]}
 */
Router.prototype.forwardPacket = function() {
  if (this.packetQueue.length === 0) {
    return [];
  }

  const [source, destination, timestamp] = this.packetQueue.shift();
  const packetKey = `${source}-${destination}-${timestamp}`;
  this.packetSet.delete(packetKey);
  this.removedPacketIndex.set(destination, (this.removedPacketIndex.get(destination) || 0) + 1);

  return [source, destination, timestamp];
};

/**
 * @param {number} destination
 * @param {number} startTime
 * @param {number} endTime
 * @return {number}
 */
Router.prototype.getCount = function(destination, startTime, endTime) {
  if (!this.destinationTimestamps.has(destination)) {
    return 0;
  }

  const timestampArray = this.destinationTimestamps.get(destination);
  const removedCount = this.removedPacketIndex.get(destination) || 0;
  const totalLength = timestampArray.length;

  if (removedCount >= totalLength) {
    return 0;
  }

  const leftBound = this.binarySearchLeft(timestampArray, startTime, removedCount);
  const rightBound = this.binarySearchRight(timestampArray, endTime, removedCount) - 1;

  if (leftBound > rightBound) {
    return 0;
  }

  return rightBound - leftBound + 1;
};

/**
 * @param {number[]} array
 * @param {number} target
 * @param {number} startIndex
 * @return {number}
 */
Router.prototype.binarySearchLeft = function(array, target, startIndex) {
  let left = startIndex;
  let right = array.length;

  while (left < right) {
    const mid = Math.floor((left + right) / 2);
    if (array[mid] < target) {
      left = mid + 1;
    } else {
      right = mid;
    }
  }

  return left;
};

/**
 * @param {number[]} array
 * @param {number} target
 * @param {number} startIndex
 * @return {number}
 */
Router.prototype.binarySearchRight = function(array, target, startIndex) {
  let left = startIndex;
  let right = array.length;

  while (left < right) {
    const mid = Math.floor((left + right) / 2);
    if (array[mid] <= target) {
      left = mid + 1;
    } else {
      right = mid;
    }
  }

  return left;
};