LeetCode刷题Medium篇Insert Delete GetRandom O(1)

题目

Design a data structure that supports all following operations in average O(1) time.

  1. insert(val): Inserts an item val to the set if not already present.
  2. remove(val): Removes an item val from the set if present.
  3. getRandom: Returns a random element from current set of elements. Each element must have the same probability of being returned.

Example:

// Init an empty set.
RandomizedSet randomSet = new RandomizedSet();

// Inserts 1 to the set. Returns true as 1 was inserted successfully.
randomSet.insert(1);

// Returns false as 2 does not exist in the set.
randomSet.remove(2);

// Inserts 2 to the set, returns true. Set now contains [1,2].
randomSet.insert(2);

// getRandom should return either 1 or 2 randomly.
randomSet.getRandom();

// Removes 1 from the set, returns true. Set now contains [2].
randomSet.remove(1);

// 2 was already in the set, so return false.
randomSet.insert(2);

// Since 2 is the only number in the set, getRandom always return 2.
randomSet.getRandom();

十分钟尝试

可以用HashSet吗?添加,删除可以,但是如何实现随机返回呢?想起来数组,数组的读取也是O(1)的,我们初始化一个随机值当作数组的索引就可以实现随机读取了。来,代码上:

class RandomizedSet {

    private Set<Integer>  set;
    
    private List<Integer> list;
    
    /** Initialize your data structure here. */
    public RandomizedSet() {
        set=new HashSet();
        list=new ArrayList();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if(set.contains(val)) return false;
        else{
            return set.add(val);
        }
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
         if(!set.contains(val)) return false;
        else{
            return set.remove(val);
        }
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        //每次随机之前,清空list,不然上次的数据都存在,即使remove也没有用
        list.clear();
        for(Integer tmp:set){
            list.add(tmp);
        }
        Random random=new Random();
        int randIndex=random.nextInt(list.size());
        return list.get(randIndex);
    }
}

/**
 * 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();
 */

Runtime: 379 ms, faster than 1.72% of Java online submissions forInsert Delete GetRandom O(1).

记得getRandom前,清空list,否则以前的数组仍然存在。但是这样不符合题目要求,因为getRandom的时候需要clear,很显然,clear方法不是O(1)的

再来。

class RandomizedSet {

    private Set<Integer>  set;
    
    private List<Integer> list;
    
    /** Initialize your data structure here. */
    public RandomizedSet() {
        set=new HashSet();
        list=new ArrayList();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if(set.contains(val)) return false;
        else{
            list.add(val);
            return set.add(val);
        }
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
         if(!set.contains(val)) return false;
        else{
            list.remove(val);
            return set.remove(val);
        }
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        Random random=new Random();
        int randIndex=random.nextInt(list.size());
        return list.get(randIndex);
    }
}

/**
 * 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后list还有数据,我判断错了,其实是remove按照索引处理了,不是值。修改后成功了。

list.remove有两个方法,如果传入的int,当索引处理,如果传入对象,比如Integer,按值处理。

/**
 * Removes a value from the set. Returns true if the set contained the specified element.
 */
public boolean remove(int val) {
    if (!set.contains(val)) return false;
    else {
        list.remove(Integer.valueOf(val));
        return set.remove(val);
    }
}

修改后写了下面一版本。成功了:
class RandomizedSet {

    private Set<Integer>  set;
    
    private List<Integer> list;
    

    
    /** Initialize your data structure here. */
    public RandomizedSet() {
        set=new HashSet();
        list=new ArrayList();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if (set.contains(val)) return false;
        else {
            list.add(val);
            return set.add(val);
        }
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
          if (!set.contains(val)) return false;
        else {
            list.remove(Integer.valueOf(val));
            return set.remove(val);
        }
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        Random random=new Random();
        int randIndex=random.nextInt(list.size());
        return list.get(randIndex);
    }
}

/**
 * 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();
 */

虽然成功了,但是时间复杂度对吗?getRandom对,insert也对,remove呢?关键是remove中的list.remove(Object obj)是O(1)吗?如果是索引,需要移动数组,不是常量时间,如果是值,也不是。这个方法也不对。

扫描二维码关注公众号,回复: 4761862 查看本文章
  • add() – takes O(1) time
  • add(index, element) – in average runs in O(n) time
  • get() – is always a constant time O(1) operation
  • remove() – runs in linear O(n) time. We have to iterate the entire array to find the element qualifying for removal,如果删除的是最后一个元素,入参是索引应该是O(1
  • indexOf() – also runs in linear time. It iterates through the internal array and checking each element one by one. So the time complexity for this operation always requires O(n) time
  • contains() – implementation is based on indexOf(). So it will also run in O(n) time
  • set(index,element)时间复杂度是O(1)

写了下面一版,主要思路是利用list的常量时间复杂度操作,set(index,element)和remove(lastindex) 完成。今天状体也不太好,所以调试了好久才发现很多问题,先把有问题代码贴出来,一个一个分析:

1. 定义了一个index,insert完之后增加,用来记录新添加元素的索引。这个是严重错误的,因为删除后,index是一直增加的,明显不对,重大错误!

2. list的add(index,element)是增加元素到指定位置,后面的元素依次后移动,不是覆盖,覆盖指定位置元素,用set(index,elemment)

3. 看下面的两行代码

   map.put(lastElement,index);
            map.remove(val);

先更新map中的索引,然后再删除。顺序刚开始写反了,如果是一个元素,等于从map移除后,又添加进入了map

class RandomizedSet {

    private Map<Integer,Integer>  map;
    
    private List<Integer> list;
    
    private int index=0;
    
    /** Initialize your data structure here. */
    public RandomizedSet() {
        map=new HashMap();
        list=new ArrayList();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if(map.containsKey(val)) return false;
        else{
            list.add(val);
            map.put(val,index++);
            return true;
        }
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
         if(!map.containsKey(val)) return false;
        else{
            //会产生不连续空间,因此记录索引,直接用最后一个元素覆盖
            int index=map.get(val);
            int lastElement=list.get(list.size()-1);
            list.set(index,lastElement);
            list.remove(list.size()-1);
            map.put(lastElement,index);
            map.remove(val);
            return true;
        }
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        Random random=new Random();
        int randIndex=random.nextInt(list.size());
        return list.get(randIndex);
    }
}

/**
 * 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();
 */

修改后代码如下:

效率有所提高

class RandomizedSet {

    private Map<Integer,Integer>  map;
    
    private List<Integer> list;
    

    
    /** Initialize your data structure here. */
    public RandomizedSet() {
        map=new HashMap();
        list=new ArrayList();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if(map.containsKey(val)) return false;
        else{
            list.add(val);
            map.put(val,list.size()-1);
            return true;
        }
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
         if(!map.containsKey(val)) return false;
        else{
            //会产生不连续空间,因此记录索引,直接用最后一个元素覆盖
            int index=map.get(val);
            int lastElement=list.get(list.size()-1);
            list.set(index,lastElement);
            list.remove(list.size()-1);
            map.put(lastElement,index);
            map.remove(val);
            return true;
        }
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        Random random=new Random();
        int randIndex=random.nextInt(list.size());
        return list.get(randIndex);
    }
}

/**
 * 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();
 */

猜你喜欢

转载自blog.csdn.net/hanruikai/article/details/85262511