文章目录
- 题目一:[只出现一次的数字](https://leetcode-cn.com/problems/single-number/)
- 题目二:[复制带随机指针的链表](https://leetcode-cn.com/problems/copy-list-with-random-pointer/)
- 题目三:[宝石与石头](https://leetcode-cn.com/problems/jewels-and-stones/)
- 题目四:[旧键盘](https://www.nowcoder.com/questionTerminal/f88dafac00c8431fa363cd85a37c2d5e)
- 题目五:[前K个高频单词](https://leetcode-cn.com/problems/top-k-frequent-words/description/)
题目一:只出现一次的数字
思路一:
采用HashSet来完成,但是这种方法效率比较低。
前提:Set具有去重的特点
因此,我们先将所有的数据存储在HashSet中,然后进行判断,如果已经存在,则删除;如果还没有的话,则存入。
然后,再通过遍历数组,判断里面的元素是什么,取出来。
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.remove(nums[i]);
}else{
set.add(nums[i]);
}
}
for(int i =0;i<nums.length;i++){
if(set.contains(nums[i])){
return nums[i];
}
}
return -1;
}
}
思路二:HashMap方式
将数据存入 HashMap 中,此时key 为 元素, Value 为 出现次数。
在存入的过程中判断该元素在HashMap 中是否存在。如果存在,value=1,并且在此基础上加上1。
否则,返回的Value 为 null,没有这个元素,此时就是第一次存入,将对应 Key - Value 存入。
取出的时候,遍历数组,通过 getValue方法,返回其 Value 值,如果等于1,就返回 对应 Key。如果没有找到那个只出现一次的 Key,就返回 -1。
class Solution {
public static int singleNumber(int[] nums) {
Map<Integer,Integer> map = new HashMap<>();
for (int i:nums){
if(map.containsKey(i)){
map.put(i,map.get(i)+1);
}else{
map.put(i,1);
}
}
for (Map.Entry<Integer,Integer>entry : map.entrySet()){
if (entry.getValue()==1) return entry.getKey();
}
return 0;
}
}
思路三:异或
异或运算满足交换律和结合律:
- a^b ^c ^ b ^ c =a ^ (b ^ b) ^ (c ^c) = a ^0 ^0 =a
public static int singleNumber(int[] nums) {
int num = 0;
for(int a :nums){
num ^=a;
}
return num;
}
题目二:复制带随机指针的链表
题目:
分析:
题目要求我们复制一个长度为 n 的链表,该链表除了每个节点有一个指针指向下一个节点外,还有一个额外的指针指向链表中的任意节点或者 null,如下图所示:
通过画的图可以看到,就是将原来的链表的内容还有指针的指向复制到另外一个地址上面。
思路一:哈希表方式解决
-
创建一个HashMap,分别存储新旧结点的地址
<key,value>对应<旧结点地址,新节点地址> -
再次遍历链表的时候把对应的值设置上
-
原节点和新节点是一一对应的关系“
- map.get(原节点),得到的就是对应的新节点
- map.get(原节点.next),得到的就是对应的新节点.next
- map.get(原节点.random),得到的就是对应的新节点.random
上节课讲过Map的常用方法:
链接:
了解完这些,代码就很简单了
public Node copyRandomList(Node head) {
Map<Node,Node> map = new HashMap<>();
Node cur= head;
while(cur != null){
Node node = new Node(cur.val);//创建一个新节点
map.put(cur,node);
cur = cur.next;
}
cur = head;//再次从头开始走
while(cur != null){
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
}
return map.get(head);
}
思路二:直接复制,然后分割链表
- 据遍历到的原节点创建对应的新节点,每个新创建的节点是在原节点后面
- 设置新链表的随机指针
- 将两个链表分离开,再返回新链表就可以了
public Node copyRandomList(Node head) {
if(head==null) {
return null;
}
Node p = head;
//1.在每个原节点后面创建一个新节点
while(p!=null) {
Node newNode = new Node(p.val);
newNode.next = p.next;
p.next = newNode;
p = newNode.next;
}
p = head;
//2.设置新节点的随机结点
while(p!=null) {
if(p.random!=null) {
p.next.random = p.random.next;
}
p = p.next.next;
}
Node dummy = new Node(-1);
p = head;
Node cur = dummy;
//3.分割链表
while(p!=null) {
cur.next = p.next;
cur = cur.next;
p.next = cur.next;
p = p.next;
}
return dummy.next;
}
题目三:宝石与石头
题目:
分析:
题意很简单,就是给你第一个关于宝石的字符串,然后看看石头字符串里面是否有,如果有,则返回有多少个;没有就返回0.
思路一:利用哈希集合
- 先把jewels字符串的字母存到哈希集合当中
- 遍历stones字符串,看看里面是否包含jewels的字母,如果包含,则利用计数器++。
public int numJewelsInStones(String jewels, String stones) {
HashSet<Character> set = new HashSet<>();
for(Character ch:jewels.toCharArray()){
set.add(ch);
}
int count = 0;
for(Character ch: stones.toCharArray()){
if(set.contains(ch)){
count++;
}
}
return count;
}
思路二:暴力解法
遍历字符串 stones,对于 stones中的每个字符,遍历一次字符串 jewels,如果其和 jewel中的某一个字符相同,则是宝石。
ublic int numJewelsInStones(String jewels, String stones) {
int count = 0;
for (int i = 0; i < stones.length(); i++) {
char stone = stones.charAt(i);
for (int j = 0; j < jewels.length(); j++) {
char jewel = jewels.charAt(j);
if (stone == jewel) {
count++;
break;
}
}
}
return count;
}
题目四:旧键盘
题目:
思路:采用HashSet方式
- 先创建一个HashSet,把键盘的实际输出存到里面
- 然后创建另外一个HashSet,坏的摁键值存到里面,方式是通过遍历预期输出的值,与我们第一个HashSet里面存放的值做比较。如果没有的话,则说明这个摁键是坏的
import java.util.*;
public class Main{
public static void func(String strExce,String strAactual){
HashSet<Character> set = new HashSet<>();
//两次转换,先转为大写,再转为数组
for(char ch:strAactual.toUpperCase().toCharArray()){
set.add(ch);
}
HashSet<Character> broken = new HashSet<>();
for(char ch: strExce.toUpperCase().toCharArray()){
if(!set.contains(ch) && !broken.contains(ch)){
System.out.print(ch);
broken.add(ch);
}
}
}
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextLine()){
String str1 = scanner.nextLine();
String str2 = scanner.nextLine();
func(str1,str2);
}
}
}
题目五:前K个高频单词
题目:
思路:
利用哈希表 & 优先队列(堆)
-
用map统计每个单词出现的次数
-
建立一个大小为K的小根堆
-
遍历Map
(1)判断堆是否满,如果不满直接入堆。
(2)判断词频是否相同,如果相同,则比较单词的大小,单词小的入堆 -
输出堆内元素,并翻转
public static List<String> topKFrequent(String[] words, int k) {
HashMap<String,Integer> map = new HashMap<>();
//1、统计每个单词出现的次数 map
for (String s : words) {
if(map.get(s) == null) {
map.put(s,1);
}else {
int val = map.get(s);
map.put(s,val+1);
}
}
//2、建立一个大小为K的小根堆
PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(k, new Comparator<Map.Entry<String, Integer>>() {
@Override
public 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()-o2.getValue();
}
});
//3、遍历Map
for (Map.Entry<String,Integer> entry : map.entrySet()) {
if(minHeap.size() < k) {
minHeap.offer(entry);
}else {
//说明堆中 已经放满了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(top.getValue().compareTo(entry.getValue()) < 0) {
minHeap.poll();
minHeap.offer(entry);
}
}
}
}
List<String> ret = new ArrayList<>();
for (int i = 0;i < k;i++) {
Map.Entry<String,Integer> top = minHeap.poll();
ret.add(top.getKey());
}
Collections.reverse(ret);
return ret;
}
总结:以上就是五道Map和Set的相关题目。