[leetcode] Array/String (Medium) - 380. Insert Delete GetRandom O(1)

brandonยท2025๋…„ 6์›” 19์ผ
0

leetcode-array/strings

๋ชฉ๋ก ๋ณด๊ธฐ
12/20

Intuition ๐Ÿค”

์ฒซ๋ฒˆ์งธ๋กœ ๋“œ๋Š” ์ƒ๊ฐ์€ ์ผ๋‹จ ํ•œ ์–ธ์–ด์˜ ๋งŽ์€ API๋“ค์„ ๊ณต๋ถ€ํ•ด์•ผ๊ฒ ๋‹ค์ด๋‹ค.

  1. Random
  • java.util.Random
  • Random random = new Random()
  • random.nextInt(max - min) + min;
  1. HashSet
  • java.util.HashSet
  • HashSet<String> names = new HashSet<>();
  • .add, .remove, .contains, .size runs in O(1) time.
  1. ArrayList
  • java.util.ArrayList
  • `ArrayList intList = new ArrayList<>();
  • .add(E) O(1), .add(index, e) O(n), .get(index) O(1), .set(index) O(1), .remove(index) O(n), .size() O(1), .isEmpty() O(1), .contains() O(n)
  1. HashMap
  • java.util.HashMap
  • .put(k,v) O(1), .get(key) O(1), .remove() -> removed element O(1), .containsKey() O(1), .containsValue() O(n), .size()

insert์™€ remove๋Š” ๊ทธ๋ƒฅ ์›๋ž˜ HashSet API๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋  ๊ฒƒ์ด๋‹ค.

getRandom()์€ ๋˜ ๋‹ค๋ฅธ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด add()์™€ remove()๋ฅผ ํ•˜๋Š” ๋™์•ˆ element๋“ค์„ ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€ํ•˜๊ณ  nextInt(size)๋ฅผ random index๋กœ ํ•˜์—ฌ ๊ทธ element๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋  ๋“ฏ ํ•˜๋‹ค.

-> ํ•˜์ง€๋งŒ remove() ๋Š” O(n)์ด์–ด์„œ ์ด ๋ฐฉ๋ฒ•์€ ์•ˆ๋œ๋‹ค.

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ HashMap<Integer, Integer>์„ ์‚ฌ์šฉํ•ด key์—๋Š” RandomSet์˜ element๋ฅผ ์ €์žฅํ•˜๊ณ  value์—๋Š” random access์— ์‚ฌ์šฉ๋  ArrayList์˜ element๋“ค์˜ index๋ฅผ ์ €์žฅํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  swap and pop๋ฅผ ์‚ฌ์šฉํ•ด ๊ทธ index์— ์žˆ๋Š” element์™€ ๋งˆ์ง€๋ง‰ index์— ์žˆ๋Š” element์™€ ๋งž ๋ฐ”๊พธ๊ณ  popํ•ด์ฃผ๋ฉด O(1)์ด๋‹ค. ์ด๋Ÿฌ๋ฉด HashMap์— ์žˆ๋Š” ์ „์— ๋งจ ๋’ค์— ์žˆ๋˜ ๊ทธ element์˜ index๋„ ๋ฐ”๊ฟ”์ฃผ์–ด์•ผ ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•œ๋‹ค๋ฉด .add(), .remove() ๋„ O(1), getRandom()๋„ O(1)์ด๋‹ค.

First Attempt

import java.util.Random; 
import java.util.HashMap; 

class RandomizedSet {
    private HashMap<Integer, Integer> hm; 
    private ArrayList<Integer> al; 
    private Random random = new Random(); 

    public RandomizedSet() {
        this.hm = new HashMap<>(); 
        this.al = new ArrayList<>(); 
    }
    
    public boolean insert(int val) {
        if (!this.hm.containsKey(val)) {
            this.al.add(val);
            int index = this.al.size(); 
            this.hm.put(val, index);
        }

        return false;
    }
    
    public boolean remove(int val) {
        if (this.hm.containsKey(val)) {
            // first, remove the element in the hashmap and get the index. 
            int index = this.hm.remove(val); 

            // second, swap and pop the element with the last element in the arraylist. 
            int temp = this.al.get(this.al.size() - 1); 
            this.al.set(this.al.size() - 1, val); 
            this.al.set(index, temp);
            this.al.remove(this.al.size() - 1);

            // third, update the index of the swapped element . 
            this.hm.put(temp, index);
            return true; 
        }

        return false;
    }
    
    public int getRandom() {
        int size = this.al.size();
        int randomIndex = random.nextInt(size);
        return this.al.get(randomIndex);
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

๋ณ„๋กœ ์ƒ๊ฐ์„ ๊นŠ๊ฒŒ ์•ˆ ํ•œ ๋‹ต์ธ ๊ฒƒ ๊ฐ™๋‹ค. remove() ๋กœ์ง์ด ์ด์ƒํ•˜๋‹ค.

Second Attempt

import java.util.Random; 
import java.util.HashMap; 

class RandomizedSet {
   private HashMap<Integer, Integer> hm; 
   private ArrayList<Integer> al; 
   private Random random = new Random(); 

   public RandomizedSet() {
       this.hm = new HashMap<>(); // <element, indexInArraylist>
       this.al = new ArrayList<>(); 
   }
   
   public boolean insert(int val) {
       if (!this.hm.containsKey(val)) {
           this.al.add(val);
           this.hm.put(val, this.al.size() - 1);
           return true;
       }

       return false;
   }
   
   public boolean remove(int val) {
       if (this.hm.containsKey(val)) { // if the element exists in the hashset,
           // first, remove the element in the hashmap and get the index. 
           int index = this.hm.remove(val); 

           // second, swap and pop the element with the last element in the arraylist. 
           int toUpdate = this.al.get(this.al.size() - 1);
           // 1. set the index with the last element
           this.al.set(index, toUpdate);

           // 2. update the index of the last element in the hash map

           this.hm.put(toUpdate, index);

           // 3. remove the last element
           this.al.remove(this.al.size() - 1);
           
           return true; 
       }

       return false;
   }
   
   public int getRandom() {
       int size = this.al.size();
       int randomIndex = random.nextInt(size);
       return this.al.get(randomIndex);
   }
}

/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet obj = new RandomizedSet();
* boolean param_1 = obj.insert(val);
* boolean param_2 = obj.remove(val);
* int param_3 = obj.getRandom();
*/

๊ฑฐ์˜ ๋‹ค ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ test case ๋ช‡๊ฐœ๊ฐ€ ํ†ต๊ณผ๊ฐ€ ์•ˆ ๋๋‹ค.

input:

["RandomizedSet","remove","remove","insert","getRandom","remove","insert"]
[[],[0],[0],[0],[],[0],[0]]

output:

[null,false,false,true,0,true,false]

expected:

[null,false,false,true,0,true,true]

์ด์œ ๋ฅผ ๋ณด๋‹ˆ ๋งŒ์•ฝ this.hm.remove(val)๋ฅผ ํ†ตํ•ด ์–ป์€ this.al์—์„œ์˜ ๊ฐ™์€ ์ˆซ์ž์˜ ์ธ๋ฑ์Šค๊ฐ€ ArrayList์˜ ๋์— ์žˆ๋‹ค๋ฉด, ๊ทธ๋ ‡๋‹ค๋ฉด ๊ทธ ์ˆซ์ž์— ๋Œ€ํ•œ ์ธ๋ฑ์Šค ๊ฐ’์„ ๋‹ค์‹œ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•ด์ค„ ํ•„์š”๊ฐ€ ์—†์—ˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋งŒ์•ฝ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•œ๋‹ค๋ฉด ๊ทธ ์ˆซ์ž๋Š” ๋‹ค์‹œ HashMap์— ๋“ค์–ด๊ฐ€๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

Third Attempt

import java.util.Random; 
import java.util.HashMap; 

class RandomizedSet {
    private HashMap<Integer, Integer> hm; 
    private ArrayList<Integer> al; 
    private Random random = new Random(); 

    public RandomizedSet() {
        this.hm = new HashMap<>(); // <element, indexInArraylist>
        this.al = new ArrayList<>(); 
    }
    
    public boolean insert(int val) {
        if (!this.hm.containsKey(val)) {
            this.al.add(val);
            this.hm.put(val, this.al.size() - 1);
            return true;
        }

        return false;
    }
    
    public boolean remove(int val) {
        if (this.hm.containsKey(val)) { // if the element exists in the hashset,
            // first, remove the element in the hashmap and get the index. 
            int index = this.hm.remove(val); 

            // second, swap and pop the element with the last element in the arraylist. 
            int toUpdate = this.al.get(this.al.size() - 1);
            // 1. set the index with the last element
            this.al.set(index, toUpdate);

            // 2. update the index of the last element in the hash map
            if (index != this.al.size() - 1)
                this.hm.put(toUpdate, index);

            // 3. remove the last element
            this.al.remove(this.al.size() - 1);
            
            return true; 
        }

        return false;
    }
    
    public int getRandom() {
        int size = this.al.size();
        int randomIndex = random.nextInt(size);
        return this.al.get(randomIndex);
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */
profile
everything happens for a reason

0๊ฐœ์˜ ๋Œ“๊ธ€