[leetcode #380] Insert Delete GetRandom O(1)


Problem


Implement the RandomizedSet class:
・ RandomizedSet() Initializes the RandomizedSet object.
・ bool insert(int val) Inserts an item val into the set if not present. Returns true if the item was not present, false otherwise.
・ bool remove(int val) Removes an item val from the set if present. Returns true if the item was present, false otherwise.
・ int getRandom() Returns a random element from the current set of elements (it's guaranteed that at least one element exists when this method is called). Each element must have the same probability of being returned.
You must implement the functions of the class such that each function works in average O(1) time complexity.
Example 1:
Input
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
Output
[null, true, false, true, 2, true, false, 2]
Explanation
・ RandomizedSet randomizedSet = new RandomizedSet();
・ randomizedSet.insert(1);//Inserts 1 to the set. Returns true as 1 was inserted successfully.
・ randomizedSet.remove(2);//Returns false as 2 does not exist in the set.
・ randomizedSet.insert(2);//Inserts 2 to the set, returns true. Set now contains [1,2].
・ randomizedSet.getRandom();//getRandom() should return either 1 or 2 randomly.
・ randomizedSet.remove(1);//Removes 1 from the set, returns true. Set now contains [2].
・ randomizedSet.insert(2);//2 was already in the set, so return false.
・ randomizedSet.getRandom();//Since 2 is the only number in the set, getRandom() will always return 2.
Constraints:
・ -2³¹ <= val <= 2³¹ - 1
・ At most 2 * 10⁵ calls will be made to insert, remove, and getRandom.
There will be at least one element in the data structure when getRandom is called.

Idea


これはおもしろい問題です.RandomizedSetというデータ構造を実現するには,集合でサポートされているinsert,removeを除いてgetRandomを実現することは困難である.JavaでサポートされているSetはindexをサポートしていないため,O(1)を用いてランダム要素をベースライブラリ関数として取得することは不可能である.したがって,setをRandomizedSetの資料構造として使用することはできない.
O(1)で実現すべきであるが,ランダムアクセスも可能であるべきであるため,インデックスとしてアクセス可能な資料構造リストを用いる.逆にlistでinsertとdeleteをO(1)として実装するには、インデックスのデータ構造を保存する必要があるため、マッピングも必要です.mapは、キーがsetに入ることを示し、listにおけるvalueの位置としてvalueを指定します.最後にrandom accessのRandomオブジェクトを作成します.
Insert関数では、与えられた数値がmapにある場合、falseが返されます.ない場合はlistに数値を追加し、mapでその数値をキーとしてlistの最後のindexをvalueとして保存します.
remove関数では、与えられた数値がmapにない場合、falseが返されます.指定した数のインデックスが見つかり、リストと地図からそれぞれ削除されます.最後のインデックスの数値が個別に保存され、削除された数値インデックスに追加されます.地図に保存されているindexも一緒に交換しなければなりません.
getRandom関数はリストサイズに対応する範囲の数値を取得し、リストから値をインデックスとして渡します.
実装クラスの問題のためか、実行するたびに結果が大きく異なります.最良の場合は結果しか添付できません.

Solution

class RandomizedSet {
    Map<Integer, Integer> map;
    List<Integer> list;
    Random random;

    public RandomizedSet() {
        map = new HashMap<Integer, Integer>();
        list = new ArrayList<Integer>();
        random = new Random();
    }

    public boolean insert(int val) {
        if (map.containsKey(val))
            return false;

        list.add(val);
        map.put(val, list.size()-1);

        return true;
    }

    public boolean remove(int val) {
        if (!map.containsKey(val))
            return false;

        int index = map.get(val);
        int lastElement = list.get(list.size()-1);
        list.remove(index);
        map.remove(val);

        if (index != list.size()) {
            list.remove(list.size()-1);
            map.remove(lastElement);
            list.add(index, lastElement);
            map.put(lastElement, index);
        }

        return true;
    }

    public int getRandom() {
        int index = random.nextInt(list.size());

        return list.get(index);
    }
}

Reference


https://leetcode.com/problems/insert-delete-getrandom-o1/