MapSet练习OJ题讲解(2易2中1难)
难易搭配,营养均衡好吸收~哈哈哈o( =•ω•= )m
Map&Set练习OJ题
- 7. OJ练习题
- 7.1 只出现一次的数字(易)
- 7.2 随机链表的复制(中)
- 7.3 宝石与石头(易)
- 7.4 旧键盘(中)
- 7.5 前K个高频单词(较难)
7. OJ练习题
7.1 只出现一次的数字(易)
136. 只出现一次的数字 - 力扣(LeetCode)
思路:
- 第一次遍历数组时,对于每个元素检查set中是否含有该元素,没有的话add进去,有的话,说明这是一个重复的数据,需要remove掉.
- 此时set中剩下一个元素,就是题目要的,但是set没有提供像peek或pop一样的方法,无法得知剩下的那个是哪个,因此需要二次遍历数组,对每个元素再检查一遍set中是否包含,包含的话,就找到了,return就好.
class Solution {public int singleNumber(int[] nums) {HashSet<Integer> set=new HashSet<>();for(int i=0;i<nums.length;i++) {if(!set.contains(nums[i])) {set.add(nums[i]);}else {set.remove(nums[i]);}}for(int i=0;i<nums.length;i++) {if(set.contains(nums[i])) {return nums[i];}}return -1;//根据题意,一定会有一个不重复的数据,因此这行不会执行,只是为了通过编译}
}
7.2 随机链表的复制(中)
138. 随机链表的复制 - 力扣(LeetCode)
题目说的深拷贝是指构建一个与原链表完全独立但是节点值与结构完全相同的链表,节点值相同好说,重点就在于把原链表内部的指向关系也拷贝下来并应用于新链表.
思路:比如原链表如图:
接下来要建立这条链表的深拷贝,首先得先创建新节点吧,创建完得连起来呀,不连起来不就丢了?其实next好办,麻烦的是还有一个random,如何将原链表节点间的random指向传递给新节点呢?
使用map,这些问题就迎刃而解了,创建完的新节点可以先不连接,通过map的映射关系也可以达到存储的作用,map不是有get方法?通过key可以获取到value,那么若将旧链表节点放在key的位置,新链表节点放在value的位置,那么map.get(oldNode)不就获取到新节点了嘛?
如图,那么接下来如何传递节点的指向关系呢?旧链表的节点之间可以使用next和random进行通讯,先获取旧节点的next和random节点,再通过map.get不就得到新节点的next和random了.如下图描绘:
代码:
public Node copyRandomList(Node head) {// 1.第一次遍历原链表,将映射关系存进map中HashMap<Node,Node> map=new HashMap<>();Node cur=head;while(cur!=null) {Node node=new Node(cur.val);//构建与原链表节点值一样的新节点map.put(cur,node);//建立映射关系cur=cur.next;}// 2.第二次遍历原链表,将节点指向关系传递给新链表cur=head;//让cur回到headwhile(cur!=null) {map.get(cur).next=map.get(cur.next);//传递节点指向关系的核心代码map.get(cur).random=map.get(cur.random);cur=cur.next;}return map.get(head);}
7.3 宝石与石头(易)
771. 宝石与石头 - 力扣(LeetCode)
思路:建立一个set,先遍历jewels数组,把宝石都存进set里,再遍历一遍stones数组,若元素在set里,那么count++,最后返回count就是宝石数量.
public int numJewelsInStones(String jewels, String stones) {HashSet<Character> set=new HashSet<>();int count=0;//遍历宝石,拿到一个个字符for(int i=0;i<jewels.length();i++) {char ch=jewels.charAt(i);set.add(ch);}for(int i=0;i<stones.length();i++) {char ch=stones.charAt(i);if(set.contains(ch)) {count++;}}return count;}
7.4 旧键盘(中)
旧键盘 (20)__牛客网
- 输出要求大写方面:既然题目要求最后输出大写的,那么不如在读取字符之前就使用String的toUpperCase()成员方法把两个字符串都变成大写的.
- 筛选方面:建立一个set1把输出的字符放进去,与输入的字符比较,不再set1里的就是坏的.
- 输出要求去重方面:那就再建立一个set2,把坏键放进去,打印时判断一下,既不在set1又不在set2的才打印.
import java.util.Scanner;
import java.util.HashSet;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseString str1 = in.nextLine();String str2 = in.nextLine();func(str1, str2);}}public static void func(String str1, String str2) {HashSet<Character> set1 = new HashSet<>();HashSet<Character> set2 = new HashSet<>();for (char ch :str2.toUpperCase().toCharArray()) { //将str2变成大写变成字符数组set1.add(ch);}for (char ch :str1.toUpperCase().toCharArray()) { //将str1变成大写变成字符数组if (!set1.contains(ch) && !set2.contains(ch)) {System.out.print(ch);set2.add(ch);}}}
}
7.5 前K个高频单词(较难)
692. 前K个高频单词 - 力扣(LeetCode)
这题的工程量还挺大的,并且很细节.
思路:
- 遍历一遍数组,统计一下每个单词的出现次数,并存进map里.基础操作,so easy:
- 后面的问题就是一个TOP K问题了,采用建堆的方式解决,小根堆还是大根堆?小根堆,大小为k的小根堆.这本身不难,细节在于你要告诉建堆程序你的比较逻辑是什么,也就是你要传入自建的比较器.那比较逻辑是什么呢?题目上说了,按频率高低排列,并且还要考虑字典序(在频率相等时,字典序小的是要排在前面的).也就是说要根据getValue()的值建立小根堆,根据getKey()建立大根堆.
上述是比较器代码,接下来开始建堆,首先根据map的前k个元素建立小根堆.然后将map的后n-k个元素依次与堆顶元素相比较,若频率比堆顶的元素更大,那么出堆顶元素,将新元素入堆;若频率相同,则比较字典序,若新元素的字典序更小,则出堆顶元素,新元素入堆:
当上述循环执行完毕,堆中的k个元素就是所求的频率最高的k个单词的键值对了.
最后,创建一个list将堆中元素的键都存进去,记得调用reverse方法将list翻转一下
完整代码:
public List<String> topKFrequent(String[] words, int k) {HashMap<String,Integer> map=new HashMap<>();for(String word:words) {if(map.get(word)==null) {//该单词第一次出现map.put(word,1);}else {int num=map.get(word);map.put(word,num+1);}}PriorityQueue<Map.Entry<String,Integer>> minHeap=new PriorityQueue<>(new Comparator<>(){@Overridepublic int compare(Map.Entry<String,Integer> o1, Map.Entry<String,Integer> o2) {if(o1.getValue().compareTo(o2.getValue())==0) { //当单词数量相同时,根据键的字典序建立大根堆return o2.getKey().compareTo(o1.getKey());}return o1.getValue().compareTo(o2.getValue());//当单词数量不同时,根据单词数量建立小根堆}});for(Map.Entry<String,Integer> entry:map.entrySet()) {if(minHeap.size()<k) {minHeap.offer(entry); //取map的前k个元素建堆}else { //将后n-k个元素依次与对顶元素比较Map.Entry<String,Integer> top=minHeap.peek();if(top.getValue().compareTo(entry.getValue())==0) {//频率相等if(top.getKey().compareTo(entry.getKey())>0) {//字典序更小的入堆minHeap.poll();minHeap.offer(entry);}}else {//频率不相等,频率更大的入堆if(entry.getValue().compareTo(top.getValue())>0) {minHeap.poll();minHeap.offer(entry);}}}}List<String> list=new ArrayList<>();for(int i=0;i<k;i++) {list.add(minHeap.poll().getKey());}Collections.reverse(list);return list;}