目标: 用Map和Set实现类:HashMap / TreeMap / HashSet / TreeSet;
学会TreeMap / TreeSet 背后的数据结构搜索树的原理和简单实现;
学会HashMap / HashSet 背后的数据结构哈希表的原理和简单实现。
应用: 检索。在大量的数据元素中找到某个特定的元素而进行的工作。
模型: 一般把搜索的数据称为关键字(key),和关键字对应的称为值(value),所以模型会有两种:(1)纯key模型 ,即Set要解决的事情,只需判断关键字是否在集合中(不能有重复),没有关联的value;(2)key-value模型 ,即Map要解决的事情,需要根据指定key找到关联的value
1、Map的简单使用
Map的常用方法:
import java.util.HashMap;
import java.util.Map;
public class MapSet {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();//HashMap是Map其中的一个类
//HashMap<Integer,String> map1=new HashMap<>();//这是一个具体的类,哈希表可以看成是一个表格
map.put("孟德","曹操");
map.put("小张","张飞");
map.put("小关","关羽");
map.put("小刘","刘备");
System.out.println(map);
System.out.println(map.get("小张"));
for(Map.Entry<String,String> entry : map.entrySet()){
System.out.println(entry.getKey()+":"+ entry.getValue());
}
}
}
2、Set的简单使用
set里的key不能重复,若出现重复,只会存储其中一个。
TreeSet底层是二叉搜索树(红黑树),会按照key的大小关系来排序,默认大小为11,2n+1扩充。
set的常用方法:
import java.util.*;
public class MapSet {
public static void main(String[] args) {
Set<Integer> set=new TreeSet<>();
set.add(1);
set.add(21);
set.add(6);
set.add(19);
System.out.println(set);
Iterator<Integer> it=set.iterator();//迭代器,遍历集合中的对象
while(it.hasNext()){
System.out.println(it.next());//打印的时候判断是否有下一个,若有下一个就打印它,it继续往下走判断是否有下一个
}
}
}
练习题:
1、找出10万个数据当中,第一个重复的元素
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
public class MapSet {
public static void main(String[] args) {
Random random=new Random();
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<10_0000;i++){//生成10万个数据,每个数据都是在0到6000,必然有重复的
list.add(random.nextInt(6000));
}
HashSet<Integer> set=new HashSet<>();
for(Integer val:list){//拿到一个数据后,遍历list,看list里是否存在这个数据,如果存在就说明它重复了,如果不存在就把这个数据添加进去
if(set.contains(val)){
System.out.println("找到了第一个重复的元素:"+val);
return;
}else{
set.add(val);
}
}
}
}
2、去除10万个数据中重复的数据
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
public class MapSet {
public static void main(String[] args) {
Random random=new Random();
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<10_0000;i++){//生成10万个数据,每个数据都是在0到6000
list.add(random.nextInt(6000));
}
HashSet<Integer> set=new HashSet();
//set.addAll(list);
for(Integer val:list){
set.add(val);//set中的这个方法就是不能将已经添加过的数据再添加一次,所以用该方法去重再合适不过了
}
System.out.println(set);
}
}
中间还有好多数据就不一一截取了~~~
3、统计出10万个数据里,重复的数据出现了几次,这些重复的数据是哪些
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class MapSet{
public static void main(String[] args) {
Random random=new Random();
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<10_0000;i++){//生成10万个数据,每个数据都是在0到6000
list.add(random.nextInt(6000));
}
HashMap<Integer,Integer> map=new HashMap();
//key—>重复出现的数据
//value—>重复的数据出现了几次
for(Integer key:list){//从list中获取数据放到map里
if(map.get(key)==null){//返回key对应的value,若为空说明这个数是第一次出现
map.put(key,1);//把value添加进去,
}else{//如果返回的value值不为空就说明,这个value对应的key已经出现过
Integer val= map.get(key);//获取当前key所对应的value值(即key之前已经出现过的次数)
map.put(key,val+1);//再在其基础上加1,就可以统计出它出现的次数
}
}
for(Map.Entry<Integer,Integer> entry : map.entrySet()){//打印map
System.out.println(entry.getKey()+"这个数字重复出现了"+entry.getValue()+"次!");
}
}
}
中间还有好多数据就不一一截取了~~~
4、给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。题目地址
思路:数组所有元素进行异或,因为相同的数字异或结果是0,0异或任何数字都是任何数字。
交换律:a ^ b ^ c <=> a ^ c ^ b;
任何数于0异或为任何数 0 ^ n => n;
相同的数异或为0: n ^ n => 0;
var a = [2,3,2,4,4];
2 ^ 3 ^ 2 ^ 4 ^ 4等价于 2 ^ 2 ^ 4 ^ 4 ^ 3 => 0 ^ 0 ^3 => 3
class Solution {
public int singleNumber(int[] nums) {
int ret=0;
for(int i=0;i<nums.length;i++){
ret^=nums[i];
}
return ret;
}
}
class Solution {
public int singleNumber(int[] nums) {
HashSet<Integer> hashSet=new HashSet<>();
//遍历数组,将数组中的数据往 hashSet里放,如果里面已经有和即将要放进去的数据
// 相等那就移除hashSet里的数据,如果没有就可以添加进去,是否移除需进行两次判断
for (int i = 0; i < nums.length ; i++) {
if(hashSet.contains(nums[i])){
hashSet.remove(nums[i]);
}else{
hashSet.add(nums[i]);
}
}
//System.out.println(hashSet);
Iterator<Integer> it=hashSet.iterator();//构造迭代器,遍历集合中的对象,使返回的数据是整型
return it.next();
}
}
5、复制带随机指针的链表。题目地址
思路:
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
class Solution {
public Node copyRandomList(Node head) {
if(head==null){return head;}
Node cur=head;
Map<Node,Node> map=new HashMap<>();
//这一步是将新旧节点的值放进map,然后根据key和value找它们之间对应的关系
while(cur!=null){
Node node=new Node(cur.val);//不为空,new一个新节点,获取节点的val
//然后将原来的节点和new的节点存到map中
map.put(cur,node);
cur=cur.next;
}
//新旧节点的映射关系已全部处理完成
cur=head;//回来继续处理next和random
while(cur!=null){//下面两句仔细看,“.next”在括号外和括号内的区别
map.get(cur).next=map.get(cur.next);
map.get(cur).random=map.get(cur.random);
cur=cur.next;
}
return map.get(head);
}
}
6、宝石和石头。给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。题目地址
class Solution {
public int numJewelsInStones(String J, String S) {
Set<Character> set=new HashSet<>();
//将用字符串表示的宝石转化为字符数组存进map
for(char j:J.toCharArray()){
set.add(j);
}
int count=0;
//接着遍历石头
for(char s:S.toCharArray()){
//看map中是否存在相同的字符,若存在那就数量加1,若不存在就继续往后遍历
if(set.contains(s)){
count++;
}
}
return count;
}
}
7、旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现。现在给出应该输入的一段文字、以及实际被输入的文字,请你列出肯定坏掉的那些键。题目地址
输入输出描述示例:
期望显示的英文:7_This_is_a_test
实际显示的英文: _hs_s_a_es
求哪些键盘坏了:7TI
按照发现顺序,在一行中输出坏掉的键。其中英文字母只输出大写,每个坏键只输出一次。题目保证至少有1个坏键。
import java.util.HashSet;
import java.util.Scanner;
public class MapSet{
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
String espected=scanner.nextLine();//7_This_is_a_test
String actual=scanner.nextLine();//_hs_s_a_es
//把实际的字符转换为大写存放到集合中
HashSet<Character> setActual=new HashSet<>();
for(char s : actual.toUpperCase().toCharArray()){ //先转换为大写再转换为字符,存到s里
setActual.add(s);
}
HashSet<Character> setBroken=new HashSet<>();
for(char ex:espected.toUpperCase().toCharArray()){ //先转换为大写再转换为字符,存到ex里
//当setActual(存的是actual)不包含ex(存放的是espected),要打印那个字符,但后续会有重复,
//所以加一条判断重复过的就不用再打印,就是把不包含的那个键先打印再放入setBroken,
//当后续出现的字符与setBroken里有重复的时,就跳过这个条件不打印也不再放入setBroken
if(!setActual.contains(ex) && !setBroken.contains(ex)){
System.out.print(ex);
setBroken.add(ex);
}
}
}
}
8、给一非空的单词列表,返回前 k 个出现次数最多的单词。返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。题目地址
// 力扣上的官方答案,先摆在这里,回头写注释!
class Solution {
public List<String> topKFrequent(String[] words, int k) {
Map<String, Integer> count = new HashMap();
for (String word: words) {
count.put(word, count.getOrDefault(word, 0) + 1);
}
PriorityQueue<String> heap = new PriorityQueue<String>(
(w1, w2) -> count.get(w1).equals(count.get(w2)) ?
w2.compareTo(w1) : count.get(w1) - count.get(w2) );
for (String word: count.keySet()) {
heap.offer(word);
if (heap.size() > k) heap.poll();
}
List<String> ans = new ArrayList();
while (!heap.isEmpty()) ans.add(heap.poll());
Collections.reverse(ans);
return ans;
}
}