1

How do i make a data structure for stock of boxes so i can implement the following methods as efficient as possible?

All boxes have a square base (length=width) but they are not necessarily cubes (height not necessarily equals to length=width).

The functions are

INSERTBOX(side, height) (side=length=width) - An insertion of a new box to the data structure

REMOVEBOX(side, height) - A deletion of a box from the stock

GETBOX(side, height) - Returns a box with minimal volume that its length is at least side and its height is at least height

CHECKBOX(side, height) - Check if there is a box in the data structure that its length is at least side and its height is at least height

The way i think to implement this is by a RB Tree with the key of volume but then if i find the box with required volume i don't know how to find among those potential boxes (the subtree of the minimal volume) the one with the minimal dimensions that meet the requirements.

Any hints? Is this the right way? Or should i think of other data structures?

Stephen C
  • 632,615
  • 86
  • 730
  • 1,096
  • Hi, welcome to StackOverflow. Try to provide a solution and we will be happy to give you a feedback. :) Here you can find a good starting tour useful tu understand how to create the perfect question: https://stackoverflow.com/tour – Ema.jar Jan 24 '19 at 22:23
  • I would use `TreeMap` of `TreeMap` so you can use `subMap()` to quickly find boxes with sizes and heights greater than given values. – Andreas Jan 24 '19 at 23:04
  • I think it would have a sense to have Box class and generate unique key from side and height. In this case problems becomes trivial - list or hashtable depends on what are you doing more often insert/remove or get/check. – Valerii Jan 24 '19 at 23:22
  • @Valerii You cannot generate an unique key from both. For example, if you are looking for a box with the volume 75, many boxes can have this volume but maybe non of them meet the requirements. If you are looking for a box with length 5 width 5 and height 3 so the volume is 75, you can find wrong box with length 1 width 1 and height 75 which is also 75 but this one does not meet the requirements. – Albert Leibnitz Jan 24 '19 at 23:52
  • @AlbertLeibnitz how about key = height.toString() + "_" + side.toString() ? – Valerii Jan 24 '19 at 23:55
  • @Valerii I cant understand how this would work – Albert Leibnitz Jan 25 '19 at 00:06
  • @AlbertLeibnitz, you have class for Box, every box has a key, side and height. StockOfBoxes class inside stores boxes as dictionary(Map in java) . If you need to add/remove box you add remove boxes from the dictionary. If you need to get the box or check you generate key from side/height and search for this key in dictionary. – Valerii Jan 25 '19 at 00:14
  • @Valerii If all boxes has volume 500 but every box with different dimensions. Will it find the one with minimal dimensions? – Albert Leibnitz Jan 25 '19 at 00:56
  • @AlbertLeibnitz, forget about volume. You have key as a string and you have object with key, side and height. When you do checkbox(20, 50) you check if your dictionary has element with key "20_50". – Valerii Jan 25 '19 at 01:29
  • @Valerii Ok but when you need to find a box with volume 500 but with specific dimensions, then what do you do? If you cant find specific box with specific dimensions you have to provide some function that can find the volume required but with minimal possible dimensions. – Albert Leibnitz Jan 25 '19 at 01:52

1 Answers1

0

I don't think that this problem requires complex data structures

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Box {

    private int side;

    private int height;

    private int volume;

    public Box(final int side, final int height) {
        this.side = side;
        this.height = height;
        this.volume = side * height;
    }

    public static String getKey(final int side, final int height) {
        return side + "_" + height;
    }

    public int getSide() {
        return side;
    }

    public int getHeight() {
        return height;
    }

    public int getVolume() {
        return height;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        final Box box = (Box) o;
        return side == box.side &&
               height == box.height;
    }

    @Override
    public int hashCode() {
        return Objects.hash(side, height);
    }
}

public class StockOfBoxes {

    private Map<String, Box> dictionary = new HashMap<>();

    public void insert(int side, int height) {
        dictionary.put(Box.getKey(side, height), new Box(side, height));
    }

    public void remove(int side, int height) {
        dictionary.remove(Box.getKey(side, height));
    }

    public Box get(int side, int height) {
        List<Box> filtered = dictionary
                .values()
                .stream()
                .filter(b -> b.getHeight() >= height && b.getSide() >= side)
                .collect(Collectors.toList());

        Box boxWithMinVolume = null;

        for (Box box: filtered) {
            if (boxWithMinVolume == null || boxWithMinVolume.getVolume() > box.getVolume()) {
                boxWithMinVolume = box;
            }
        }

        return boxWithMinVolume;
    }

    public boolean check(int side, int height) {
        return dictionary
            .values()
            .stream()
            .anyMatch(b -> b.getHeight() >= height && b.getSide() >= side);
    }
}

Time complexity of the methods:

insert - O(1)

remove - O(1) (see this)

get - O(n)

check - O(n)

I don't think that any data structure will allow you to improve this numbers. Even if you improve get/check somehow it will cause worse time complexity for insert.

Valerii
  • 1,769
  • 2
  • 9
  • 23
  • But the OP said that `get` and `check` aren't looking for exact size. It says "length is at least **side** and height is at least **height**." Your code is looking for exact matches. So if you have a box that has side=10 and height=5, it should match a query for side=8 and height=3, or side=10 and height=4. – Jim Mischel Jan 25 '19 at 15:44
  • @JimMischel I updated an answer. Check works the same was as get. I missed that part, but it doesn't change whole point. – Valerii Jan 25 '19 at 17:47
  • Well, sure, treating the map as a sequential list works, but then what's the point of the map? Every search requires an O(n) scan of the list. Might as well just use a `List` and be done with it. I'll remove my downvote because your response does answer the question, but I don't think it's a particularly good solution. – Jim Mischel Jan 25 '19 at 21:36
  • @JimMischel, List will not allow you to have O(1) for remove. It depends on how important it is. – Valerii Jan 25 '19 at 21:51
  • I figured out how to implement this with all methods running O(logn) worst time. Just use RB tree sorted by side and maintain an extra attribute - max height. Each node x has an attribute x.maxHeight, which is the maximum of height in the subtree rooted at x. So you can travel down the tree and easily find needed box. – Albert Leibnitz Jan 26 '19 at 02:12
  • Its simple. Its the same idea as of an interval tree. You can see here [https://www.geeksforgeeks.org/interval-tree/]. Just use max as max height in the subtree rooted at the node you are looking at. and low as side and high as height. So in the example in the link if you are looking for [11,14] (side 11, height 14), you go left, right, bingo. – Albert Leibnitz Jan 26 '19 at 12:44
  • @Valerii I am writing the code now and it doesn't work :D, or :(. I think its impossible to implement all methods in O(logn). The thing is, i cant use hashmaps. I must implement this with RB tree. This is the instructions. And i wonder now, whats the best time complexity implementation possible with RB tree. – Albert Leibnitz Jan 26 '19 at 23:35