Map和Set
Map和Set是一种专门用来搜索的数据结构。这俩都是接口,并不是具体实现类。
模型
一般把搜索的数据称为关键字(Key),把与关键字对应的称为值(Value),将其称为Key-Value键值对,产生两种模型:
纯Key模型:比如存储英文单词,对应的是Set接口。
Key-Value模型:比如要存储英文单词,以及单词对应的出现次数。对应的是Map接口。
Map
1️⃣Map是一个接口,不能直接new对象,可以new其实现类:TreeMap,HashMap
2️⃣Map中的key不可以重复,value可以重复,如果添加的键值对中的key重复了,会覆盖掉原有的键值对
3️⃣Map中键值对的key不能直接修改,value可以修改,如果要修改key,需要将键值对删除掉,再添加新的键值对
4️⃣TreeMap和HashMap的对比:TreeMap关于key有序,HashMap无序;TreeMap中存储的元素必须是可比较的,如果是自定义类型,需要传一个比较器。
TreeMap和HashMap对比
Entry<K,V>
Map是一个接口,其中有一个内部接口:
⭕️Entry<K,V>相对于Map<K,V>的关系就相当于Node相对于LinkedList。所以Entry<K,V>是真正存储键值对的。所以当Map的实现类实现了Map接口的时候,实现类中也得有个内部类实现Entry<K,V>接口。
比如TreeMap中的内部类Entry<K,V>:这个内部类其实就是真正存储键值对的节点,
常用方法
Map中的常用方法:
1️⃣V get(Object key):返回key对应的value,如果key不存在,返回null
2️⃣V getOrDefalult(Object key,V defaultValue):如果key存在,返回对应的value,如果key不存在,返回defaultValue。
3️⃣V put(K key,V value):设置一组键值对,如果key在map中已经存在了,则会覆盖掉之前的键值对,并返回旧的键值对的Value(因为Map中Key不能重复)。如果key在map中不存在,返回的是null。
4️⃣V remove(Object key):删除key及其对应的Value这组键值对,如果原map中有对应的key,则删除并返回对应的value,如果原map中没有对应的value,则返回的是null
5️⃣Set keySet() : 返回所有key的不重复集合:将map中所有的key存储到Set中
6️⃣Collection values():返回所有value的可重复集合
7️⃣Set<Map.Entry<K,V>> entrySet() : 返回所有的key-value映射关系,返回的是一个集合,集合中的元素是一个个对象,对象中存储的是原map中的key和value
8️⃣boolean containsKey(Object key):判断map中是否包含key
9️⃣boolean coontainsValue(Object value) :判断map中是否包含value
Set
Set和Map的区别在于Set继承自Collection接口,Set中存储的是Key,Map中存储的是Key-Value
但其实TreeSet和TreeMap底层用的是一样的结构,都是红黑树,HashSet和HashMap底层用的结构是一样的,都是哈希桶。
在源码中:
创建Set实例实际创建的还是Map实例。
1️⃣Set中存储的是Key,Map中存储的是Key-Value,且Set中的key不能有重复的
2️⃣Set实现类:TreeSet和HashSet,TreeSet中key是有序的,HashSet中key是无序的。
3️⃣Set底层是使用的Map
4️⃣Set中的key不能修改,如果要修改需先删除再插入
5️⃣TreeSet中不能插入null,因为插入null无法比较
TreeSet和HashSet对比
常用方法
1️⃣boolean add(E e):添加元素到集合中,如果集合中有则不添加成功
2️⃣void clear():清空集合
3️⃣boolean contains(Object o):判断集合中是否有该元素
4️⃣boolean remove(Object o):删除集合中的元素
5️⃣int size():返回set中元素个数
6️⃣ boolean isEmpty(): 返回集合是否为空
7️⃣Object[] toArray():将集合中的元素转换为数组返回
8️⃣boolean containsAll(Collection<?> c):判断集合c中的元素是否在set中全部存在,是返回true,否返回false
9️⃣boolean addAll(Collection<? extends E> c),将集合c中的元素全部添加到set中,还是会保证元素不重复。
OJ练习
只出现一次数字
⭕️==方法一:==使用Set,遍历数组,集合中没有当前数字则进集合,有当前数字则出集合,那最后出现两次的数字就不会在集合中存在,只剩出现一次的数字。
class Solution {
public int singleNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int i=0;i<nums.length;i++){
if(set.contains(nums[i])){
set.remove(nums[i]);
}else{
set.add(nums[i]);
}
}
for(Integer x:set){
return x;
}
return 0;
}
}
⭕️==方法二:==遍历数组,把所有数字按位异或到一个数字num上,num初始为0。
class Solution {
public int singleNumber(int[] nums) {
int num = 0;
for(int i=0;i<nums.length;i++){
num ^= nums[i];
}
return num;
}
}
复制带随机指针的链表
⭕️==方法一:==使用map存储新旧节点的地址,这样新旧节点的对应关系就保存了下来,后续修改next指针域和random指针域就可以根据map中存储的信息修改。
class Solution {
public Node copyRandomList(Node head) {
if(head==null){
return null;
}
Node cur = head;
Map<Node,Node> map = new HashMap<>();
//将旧节点和新节点的地址一一对应存到map中
while(cur!=null){
Node node = new Node(cur.val);
map.put(cur,node);
cur = cur.next;
}
cur = head;
while(cur!=null){
//对于HashMap,get的参数是null则返回null
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}
🍎时间复杂度:O(N),空间复杂度:O(N)
⭕️方法二:
1️⃣每创建一个新的节点就插入到链表的旧结点之后,让旧结点和新节点有一一对应关系(前是旧结点,后是新节点)。
2️⃣然后遍历链表,修改新节点的random指针域。这时候先不能修改next指针域,一修改next对应关系就没了。
3️⃣再次遍历链表,将新节点从原链表中一个一个移出,并连接到新链表的最后。
class Solution {
public Node copyRandomList(Node head) {
if(head==null){
return null;
}
Node cur = head;
//把新的节点加到旧节点之后
while(cur!=null){
Node newNode = new Node(cur.val);
newNode.next = cur.next;
cur.next = newNode;
cur = cur.next.next;
}
cur = head;
//修改radom指针域
while(cur!=null){
Node newNode = cur.next;
Node nextNode = cur.next.next;
newNode.random = (cur.random == null ? null : cur.random.next);
cur = nextNode;
}
//修改next指针域
cur = head;
Node newHead = null;
Node lastNode = null;
while(cur!=null){
Node newNode = cur.next;
Node nextNode = cur.next.next;
//把新节点从原链表拆下来
cur.next = nextNode;
//把拆下来的新节点连接到新链表末尾
if(newHead == null){
newHead = newNode;
lastNode = newHead;
}else{
lastNode.next = newNode;
lastNode = newNode;
}
cur = nextNode;
}
return newHead;
}
}
🍎时间复杂度:O(N),空间复杂度:O(1)
宝石与石头
class Solution {
public int numJewelsInStones(String jewels, String stones) {
Set<Character> set = new HashSet<>();
for(int i=0;i<jewels.length();i++){
set.add(jewels.charAt(i));
}
int count = 0;
for(int i=0;i<stones.length();i++){
if(set.contains(stones.charAt(i))){
count++;
}
}
return count;
}
}
坏键盘打字
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
String str1 = scanner.nextLine();
String str2 = scanner.nextLine();
str2 = str2.toUpperCase();
str1 = str1.toUpperCase();
Set<Character> set = new HashSet<>();
for(int i=0;i<str2.length();i++){
set.add(str2.charAt(i));
}
Set<Character> set1 = new HashSet<>();
for(int i=0;i<str1.length();i++){
char tmp = str1.charAt(i);
if(!set.contains(tmp) && !set1.contains(tmp)){
set1.add(tmp);
System.out.print(tmp);
}
}
}
}