序言:这题可能相对之前的题稍微代码量大一些,但是别急,我们只有理清思路,其实实现起来也挺简单,重在理解,我在实现代码部分特地还增加了一些变量方法的详细解释,担心有人不懂ArrayList集合和哈希集合操作,在最后还进行了补充,篇幅较长,望君细细品读。
实现RandomizedSet
类:
RandomizedSet()
初始化 RandomizedSet
对象bool insert(int val)
当元素 val
不存在时,向集合中插入该项,并返回 true
;否则,返回 false
。bool remove(int val)
当元素 val
存在时,从集合中移除该项,并返回 true
;否则,返回 false
。int getRandom()
随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)
。
示例:
输入 ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] [[], [1], [2], [2], [], [1], [2], []] 输出 [null, true, false, true, 2, true, false, 2] 解释 RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。 randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。 randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。 randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。 randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。 randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。 randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
我们需要实现O(1)时间复杂度的时间插入、删除和获取随机元素 单独使用ArrayList集合或哈希集合,都没有办法实现,因为哈希集合不方便实现获取随机元素,ArrayList集合查询操作无法满足条件。这时不妨想想,哈希集合查询元素的时间复杂度为O(1),ArrayList集合删除最后一个元素,以及插入一个元素到最后都为O(1),且可以实现随机访问元素。 这样一想,我们是不是可以将二者进行结合呢?
因此我们先来进行对象初始化,需要定义3个对象:
(1)定义ArrayList集合对象:用于存储元素和删除元素
(2)定义哈希集合对象:用于查询元素
(3)定义随机数对象用于获取随机索引
然后进行方法的构思:
insert
方法首先检查值是否已经存在于HashMap
中,如果存在,则返回false
。如果不存在,将值添加到ArrayList
的末尾,并在HashMap
中记录这个值到其索引的映射。remove
方法检查值是否存在于HashMap
中,如果不存在,则返回false
。如果存在,找到这个值的索引,将ArrayList
的最后一个元素移动到要删除的元素的位置,并更新HashMap
中的索引。然后,从ArrayList
中删除最后一个元素,并从HashMap
中删除对应的映射。getRandom
方法使用Random
对象生成一个随机索引,并从ArrayList
中返回该索引处的元素。import java.util.ArrayList; // 导入ArrayList类
import java.util.HashMap; // 导入HashMap类
import java.util.Random; // 导入Random类
// 定义RandomizedSet类
class RandomizedSet {
// 使用HashMap存储数字到索引的映射
private HashMap numToIndex;
// 使用ArrayList存储集合中的所有数字
private ArrayList nums;
// Random对象用于生成随机数
private Random rand;
// 构造函数,初始化HashMap、ArrayList和Random对象
public RandomizedSet() {
numToIndex = new HashMap<>();
nums = new ArrayList<>();
rand = new Random();
}
// 插入操作,如果元素已存在则返回false,否则插入并返回true
public boolean insert(int val) {
// 检查元素是否已存在
if (numToIndex.containsKey(val)) {
return false;
}
// 将元素添加到ArrayList的末尾,并记录其索引
numToIndex.put(val, nums.size());
nums.add(val);
return true;
}
// 删除操作,如果元素不存在则返回false,否则删除并返回true
public boolean remove(int val) {
// 检查元素是否存在
if (!numToIndex.containsKey(val)) {
return false;
}
// 获取要删除元素的索引
int removeIndex = numToIndex.get(val);
// 获取ArrayList的最后一个元素
int lastElement = nums.get(nums.size() - 1);
// 将最后一个元素放到要删除的元素的位置
nums.set(removeIndex, lastElement);
// 更新最后一个元素在HashMap中的索引
numToIndex.put(lastElement, removeIndex);
// 从ArrayList中移除最后一个元素
nums.remove(nums.size() - 1);
// 从HashMap中移除要删除元素的映射
numToIndex.remove(val);
return true;
}
// 随机获取一个元素
public int getRandom() {
// 生成一个随机索引
int randomIndex = rand.nextInt(nums.size());
// 返回ArrayList中随机索引处的元素
return nums.get(randomIndex);
}
}
import java.util.ArrayList; // 导入ArrayList类
import java.util.HashMap; // 导入HashMap类
import java.util.Random; // 导入Random类
// 定义RandomizedSet类
class RandomizedSet {
// 使用HashMap存储数字到索引的映射
private HashMap numToIndex;
// 使用ArrayList存储集合中的所有数字
private ArrayList nums;
// Random对象用于生成随机数
private Random rand;
// 构造函数,初始化HashMap、ArrayList和Random对象
public RandomizedSet() {
numToIndex = new HashMap<>();
nums = new ArrayList<>();
rand = new Random();
}
// 插入操作,如果元素已存在则返回false,否则插入并返回true
public boolean insert(int val) {
// 检查元素是否已存在
if (numToIndex.containsKey(val)) {
return false;
}
// 将元素添加到ArrayList的末尾,并记录其索引
numToIndex.put(val, nums.size());
nums.add(val);
return true;
}
// 删除操作,如果元素不存在则返回false,否则删除并返回true
public boolean remove(int val) {
// 检查元素是否存在
if (!numToIndex.containsKey(val)) {
return false;
}
// 获取要删除元素的索引
int removeIndex = numToIndex.get(val);
// 获取ArrayList的最后一个元素
int lastElement = nums.get(nums.size() - 1);
// 将最后一个元素放到要删除的元素的位置
nums.set(removeIndex, lastElement);
// 更新最后一个元素在HashMap中的索引
numToIndex.put(lastElement, removeIndex);
// 从ArrayList中移除最后一个元素
nums.remove(nums.size() - 1);
// 从HashMap中移除要删除元素的映射
numToIndex.remove(val);
return true;
}
// 随机获取一个元素
public int getRandom() {
// 生成一个随机索引
int randomIndex = rand.nextInt(nums.size());
// 返回ArrayList中随机索引处的元素
return nums.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();
*/
下面是对每个部分的详细解释:
private HashMap numToIndex;
:一个哈希表,用于存储集合中每个数字到其在ArrayList
中的索引的映射。这使得我们可以快速地访问和更新元素的位置。private ArrayList nums;
:一个动态数组,用于存储集合中的所有元素。这允许我们快速地随机访问任何一个元素。private Random rand;
:一个随机数生成器,用于getRandom
方法中生成随机索引。public RandomizedSet()
:构造函数初始化了HashMap
和ArrayList
,并创建了一个随机数生成器实例。public boolean insert(int val)
:向集合中插入一个新元素。
HashMap
中(即集合中是否已包含该值)。false
表示插入失败。ArrayList
的末尾,并在HashMap
中记录这个新元素的值和它在ArrayList
中的索引。true
表示插入成功。public boolean remove(int val)
:从集合中删除一个元素。
HashMap
中。false
表示删除失败。ArrayList
中的索引,然后执行以下步骤:
ArrayList
最后一个元素与要删除的元素交换位置。HashMap
中最后一个元素的新索引。ArrayList
中删除最后一个元素。HashMap
中移除被删除元素的映射。true
表示删除成功。public int getRandom()
:随机返回集合中的一个元素。
Random
对象生成一个0到ArrayList长度
减1的随机索引。ArrayList
中该索引处的元素。insert
和remove
操作的平均时间复杂度是O(1),因为哈希表的插入、查找和删除操作都是O(1),而数组元素的交换和删除操作也是O(1)。getRandom
操作的时间复杂度是O(1),因为生成随机数和随机访问数组元素都是常数时间操作。这种实现确保了RandomizedSet
的所有操作都能以平均常数时间完成,同时支持随机访问元素。
基于哈希表: HashSet
基于哈希表实现,通过哈希函数将元素分布到不同的桶(bucket)中。
元素唯一性: 只允许存储不重复的元素,即集合中的每个元素都是唯一的。
无序性: 元素在集合中没有特定的顺序,它们是无序的。
快速查找: 由于基于哈希表,HashSet
提供了快速的查找能力,平均时间复杂度为 O(1)。
动态扩容: 当元素数量增加到一定程度时,HashSet
会进行扩容以保持操作的效率。
常用操作: 提供了添加(add
)、删除(remove
)、检查存在性(contains
)等操作。
内存占用: 通常比 ArrayList
占用更多的内存,因为需要存储额外的哈希表信息。
基于动态数组: ArrayList
是一个动态数组的实现,可以动态地增长和缩小。
元素有序: 元素在 ArrayList
中按照它们被插入的顺序排列,即元素是有序的。
快速随机访问: 允许快速随机访问任何位置的元素,时间复杂度为 O(1)。
插入和删除操作: 在列表末尾添加元素是 O(1) 的操作,但在中间或开始插入或删除元素可能需要 O(n) 的时间,因为可能需要移动其他元素。
容量调整: 当 ArrayList
的容量不足以容纳新元素时,会自动调整容量,这涉及到创建一个新的数组和复制旧元素。
常用操作: 提供了添加(add
)、删除(remove
)、获取元素(get
)、设置元素(set
)等操作。
内存连续性: ArrayList
的内存是连续的,这使得它在遍历和随机访问时非常高效。
ArrayList
适合于需要快速随机访问元素和保持元素插入顺序的场景。选择使用哪种集合类型取决于具体的应用需求和性能考虑。理解每种集合的特点可以帮助开发者做出更合适的选择。
ArrayList
和HashMap
是Java集合框架中的两个非常重要的类,分别提供了动态数组和哈希表的实现。下面是它们的基本操作:
添加元素
add(E e)
: 向列表末尾添加一个元素。add(int index, E element)
: 在指定位置插入一个元素。获取元素
get(int index)
: 返回指定位置的元素。设置元素
set(int index, E element)
: 用指定元素替换列表中指定位置的元素。删除元素
remove(Object o)
: 移除列表中第一次出现的指定元素。remove(int index)
: 删除指定位置的元素。大小操作
size()
: 返回列表中的元素数量。遍历
清空列表
clear()
: 移除列表中的所有元素。添加键值对
put(K key, V value)
: 将指定的值与此映射中的指定键关联。获取值
get(Object key)
: 返回指定键所映射的值。删除键值对
remove(Object key)
: 如果存在一个键的映射关系,则将其从映射中移除。检查存在性
containsKey(Object key)
: 如果映射包含键的映射关系,则返回true。键集操作
keySet()
: 返回映射中包含的键的Set视图。值集操作
values()
: 返回映射中包含的值的Collection视图。条目集操作
entrySet()
: 返回映射中包含的键值映射关系的Set视图。大小操作
size()
: 返回映射中键值对的数量。清空映射
clear()
: 移除映射中的所有键值对。遍历
以下是一些使用ArrayList
和HashMap
的示例代码:
import java.util.ArrayList;
import java.util.HashMap;
public class Example {
public static void main(String[] args) {
// ArrayList示例
ArrayList list = new ArrayList<>();
list.add("Apple"); // 添加元素
System.out.println(list.get(0)); // 获取元素
list.set(0, "Banana"); // 设置元素
list.remove("Apple"); // 删除元素
System.out.println(list.size()); // 获取大小
list.clear(); // 清空列表
// HashMap示例
HashMap map = new HashMap<>();
map.put(1, "Apple"); // 添加键值对
String value = map.get(1); // 获取值
map.remove(1); // 删除键值对
boolean contains = map.containsKey(1); // 检查键是否存在
System.out.println(map.size()); // 获取大小
map.clear(); // 清空映射
}
}
这些基本操作为使用ArrayList
和HashMap
提供了强大的灵活性,使得它们成为处理集合数据时的首选工具。