This is [030 on LeetCode. Insert, delete and random access are O(1) containers], and the difficulty is [medium]
subject
Design a data structure that supports the following operations under the average time complexity O(1):
- insert(val): returns true when the element val does not exist, and inserts the item into the collection; otherwise, returns false.
- remove(val): returns true when the element val exists, and removes the item from the collection; otherwise, returns false.
- getRandom: returns an item in an existing collection at random. Each element should have the same probability of being returned.
Problem solution
Train of thought analysis
According to the first two conditions of the meaning of the question, the time complexity of insertion and deletion is O(1). It is easy to think of using a hash table. The insertion, deletion and search of an element only need O(1). The last condition of the meaning of the question returns a data randomly, and the probability of each data returned is required to be equal. Obviously, the hash table does not meet this condition, The following is illustrated by implementing HashMap of hash table in java as an example
The bottom layer adopts array + linked list + red black tree. In addition to storing elements, each element of the array also has a reference variable to point to the next element (linked list). It finds the element by finding the remainder of the hash value of the element (hash function) to determine the position of the element in the array. There may be a linked list in the position of the array to store other elements (the hash value is the same), so it cannot return each value with equal probability.
Return each element with equal probability. Obviously, the array meets this condition. Assuming that the length of the array is n, the probability of each element being returned is 1/n
To sum up, we should design a container containing hash tables and arrays
Because each element needs to be returned with equal probability, the data can only be stored in the array. You can use the java dynamic array ArrayList, and it meets the first and third conditions, but it does not meet the second condition. For example, to delete an element, you need to do the following two operations
- Traverse the array to find the element to be deleted. When deleting the last element, you need to traverse the array. The time complexity is O(n). When deleting the first element, it is O(1)
- When the element to be deleted is found, if the last element is deleted, the time complexity is O(1). When the first element is deleted, all elements of the first element need to be moved forward, and the time complexity is O(n)
To sum up, the time complexity of deleting elements of the array is O(n), so we need to use HashMap to solve the above two problems
Solve the first problem
The key of HashMap is used to store the element, and the value is used to store the index of the element in the array. Therefore, when deleting the element, it is not necessary to traverse the array to find the deleted element. The index of the deleted element can be obtained through the map key, and the time complexity is O(1)
Solve the second problem
When we find the location of the deleted element in the array, we only need to replace its value body with the value of the last element of the array, and then delete the last element. The time complexity of deleting the last element of the array is O(1), and there is no need to move the element. Since the value of the last element of the array is assigned to the location where the element is deleted, the index of the last element of the HashMap needs to be changed
code implementation
public class RandomizedSet { private HashMap<Integer, Integer> numToLocation; private ArrayList<Integer> nums; Random random = new Random(); public RandomizedSet() { numToLocation = new HashMap<>(); nums = new ArrayList<>(); } public boolean insert(Integer val) { // Determine whether the inserted element exists and the array through the map if (numToLocation.containsKey(val)){ return false; } // There is no execution logic // Record the position of the inserted element in the array. Each insertion is at the end of the array, so the index is the length of the array numToLocation.put(val, nums.size()); // Adds an element to the end of the array nums.add(val); return true; } public boolean remove(Integer val) { // Judge whether the deleted element exists in the array through the map if (!numToLocation.containsKey(val)) { return false; } // There is execution logic // Get the index of deleted elements in the array through map Integer location = numToLocation.get(val); // Gets the last element of the array Integer lastNum = nums.get(nums.size() - 1); // Replace the value of the element to be deleted in the array with the last element of the array nums.set(location, lastNum); // Deletes the last element of the array nums.remove(nums.size() - 1); // Change the index of the last element of the array into the index of the deleted element through map numToLocation.put(lastNum, location); // Delete the index of the element to be deleted from the map numToLocation.remove(val); return true; } public Integer getRandom() { // Assuming that the array length is n, a random number between [0 ~ n-1] is generated int i = random.nextInt(nums.size()); return nums.get(i); } }