【Java 数据结构与算法】-Map和Set的OJ题

news/2024/11/24 1:39:48/

作者:学Java的冬瓜
博客主页:☀冬瓜的主页🌙
专栏:【Java 数据结构与算法】
内容:1、只出现一次的数字 4、复制带随机指针的链表 5、石头里选出宝石 6、坏键盘打字

在这里插入图片描述

文章目录

  • 一、正确选择Map和Set的实现类示例
    • 1、统计10w个数中不重复的数据,这些数据0-99
    • 2、打印10w个数中第一个重复的数据
    • 3、统计10w个数据当中,每个数据出现的次数
  • 二、Map和Set的OJ题
    • 1、只出现一次的数字
      • 法一:异或
      • 法二:Set解决
      • 法三:Map解决
    • 2、只出现一次的数字II
    • 3、只出现一次的数字 III
      • 法一:Set解决
      • 法二:Map解决
    • 4、复制带随机指针的链表
    • 5、石头里选出宝石
      • 法一:两个for循环
      • 法二:Set的contains方法
    • 6、坏键盘打字

一、正确选择Map和Set的实现类示例

Main方法:

	public static void main(String[] args) {Random random = new Random();int[] arr = new int[10_0000];for (int i = 0; i < arr.length; i++) {arr[i] = random.nextInt(100);}f1(arr);

1、统计10w个数中不重复的数据,这些数据0-99

  • 使用Set容器来装这些数据
public static void f1(int[] arr){Set<Integer> set = new HashSet<>();  // TreeSet也行,只是效率上看HashSet更快for (int i = 0; i < arr.length; i++) {set.add(arr[i]);}System.out.println(set);}

2、打印10w个数中第一个重复的数据

  • 创建一个Set容器,如果当前元素不在Set中,则add,如果已经在Set中,那么这个数据就是第一个重复的数据,打印然后直接return。
	public static void f2(int[] arr){Set<Integer> set = new HashSet<>();for (int i = 0; i < arr.length; i++) {if(!set.contains(arr[i])){// System.out.print(arr[i]+" ");  //便于观察set.add(arr[i]);}else {System.out.println();System.out.println(arr[i]);return;}}}

3、统计10w个数据当中,每个数据出现的次数

  • 遍历原来的数据,把a[i]作为map的key存入map中。遍历时,如果map中还没有以当前a[i]为key的数据,那就存入value=1,如果已经有了key对应的value,那就将value+1再存入。
	public static void f3(int[] arr){// 第一步:遍历原来的数据,统计每个数据出现的次数Map<Integer,Integer> map = new HashMap<>();for (int i = 0; i < arr.length; i++) {int key = arr[i];// 第二步:判断以arr[i]作为key的值是否已经存在if (map.get(key) == null) {map.put(key,1);}else {int val = map.get(key);map.put(key, val + 1);}}System.out.println(map);}

二、Map和Set的OJ题

1、只出现一次的数字

链接:【LeetCode136.只出现一次的数字】

法一:异或

  • 相同的数异或结果为0,0和非0数异或结果为这个数。因为只有一个数只出现了1次,其它数都出现了两次,所以把数组中所以元素拿来异或,异或的结果就是这个只出现一次的数。
// 法一:使用异或
class Solution {public int singleNumber(int[] nums) {int x = nums[0];for(int i=1; i<nums.length; i++){x^=nums[i];}return x;}
}

法二:Set解决

  • 。因为只有一个数只出现了1次,其它数都出现了两次。遍历这个数组时,当我们遇到一个不在Set中的数据时,就把数据add进Set,当遇到一个在Set中的数据时,把这个数从Set中移除,这样Set中就只剩下了只出现一次的数据。
// 法二:使用集合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.add(nums[i]);}else{set.remove(nums[i]);}}for(Integer x : set){return x;}return -1;}
}    

法三:Map解决

  • 先遍历一遍数组,使用Map统计各个数据出现的次数,再把Map拿来遍历,如果当前数据的Key对应的value == 1,那结果就是这个数据对应的key。
// 法三:使用集合Map
class Solution {public int singleNumber(int[] nums) {Map<Integer,Integer> map = new HashMap<>();for(int i=0; i<nums.length; i++){int key = nums[i];if(map.get(key) == null){map.put(key,1);}else{int val = map.get(key);map.put(key,val+1);}}// 开始遍历map,先把map变成setSet<Map.Entry<Integer,Integer>> entrySet = map.entrySet();for(Map.Entry<Integer,Integer> entry : entrySet){if(entry.getValue() == 1){return entry.getKey();}}return -1;}
}    

2、只出现一次的数字II

链接:【LeetCode137.只出现一次的数字II】

// 使用Map解决
class Solution {public int singleNumber(int[] nums) {Map<Integer,Integer> map = new HashMap<>();for(int i=0; i<nums.length; i++){int key = nums[i];if(map.get(key) == null){map.put(key,1);}else{int val = map.get(key);map.put(key,val+1);}}// 开始遍历map,先把map变成setSet<Map.Entry<Integer,Integer>> entrySet = map.entrySet();for(Map.Entry<Integer,Integer> entry : entrySet){if(entry.getValue() == 1){return entry.getKey();}}return -1;}
}

3、只出现一次的数字 III

法一:Set解决

// 使用Set解决
class Solution {public int[] singleNumber(int[] nums) {// 法一:使用SetSet<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]);}}List<Integer> list = new ArrayList<>();for(Integer x : set){list.add(x);}int[] ret = new int[list.size()];for(int i=0; i<list.size(); i++){ret[i] = list.get(i);}

法二:Map解决

链接:【LeetCode260.只出现一次的数字III】

// 使用map解决
class Solution {public int[] singleNumber(int[] nums) {// 用Map统计每个元素出现的次数Map<Integer,Integer> map = new HashMap<>();for(int i=0; i<nums.length; i++){int key = nums[i];if(map.get(key) == null){map.put(key,1);}else{int val = map.get(key);map.put(key,val+1);}}// 把结果放进ArrayListList<Integer> list = new ArrayList<>();int cnt = 0;Set<Map.Entry<Integer,Integer>> entrySet = map.entrySet();for(Map.Entry<Integer,Integer> entry : entrySet){if(entry.getValue() == 1){list.add(entry.getKey());}} 注意:遍历map时使用lambda表达式更简单// map.forEach((k,v)->{//     if(v == 1){//         list.add(k);//     }// });// 把结果放进数组int[] ret = new int[list.size()];for(int i=0; i<list.size(); i++){ret[i] = list.get(i);}return ret;}
}

4、复制带随机指针的链表

链接:【LeetCode138.复制带随机指针的链表】

/*
// Definition for a Node.
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) {Map<Node,Node> map = new HashMap<>();Node cur = head;// 第一步:第一次遍历链表,申请新的空间,传节点的val值,把旧节点作为key,对应位置的新节点作为value存入map。while(cur != null){Node newNode = new Node(cur.val);map.put(cur,newNode);cur = cur.next;}// 第二步:第二次遍历链表,把新节点的next和random引用根据map指向正确的节点cur = head;while(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);}
}
  • 第一次遍历链表,申请新的空间,传节点的val值,把旧节点作为key,对应位置的新节点作为value存入map。
    第二次遍历链表,代码是map.get(cur).next = map.get(cur.next);当前cur是原节点,通过map和cur,map.get(cur) 就是原节点对应的新节点,而map.get(cur.next)是原节点的下一个节点对应位置的新节点。
    第三步,返回原链表对应头节点的新链表的头节点。

如果还有问题,在第二次开始遍历时,见下图:
在这里插入图片描述

5、石头里选出宝石

链接:【LeetCode771.宝石与石头】

法一:两个for循环

//法一:使用两个for循环,借助ArrayList集合(或者使用个count计数也可)
class Solution {public int numJewelsInStones(String jewels, String stones) {List<Character> list = new ArrayList<>();for (int i = 0; i < jewels.length(); i++) {char a = jewels.charAt(i);for (int j = 0; j < stones.length(); j++) {char b = stones.charAt(j);if (a == b) {list.add(a);}}}return list.size();

法二:Set的contains方法

// 法二:使用Set的contains方法
class Solution {public int numJewelsInStones(String jewels, String stones) {int count = 0;Set<Character> set = new HashSet<>();for(int i=0; i<jewels.length(); i++){char a = jewels.charAt(i);set.add(a);}for(int i=0; i<stones.length(); i++){char b = stones.charAt(i);if(set.contains(b)){count++;}}return count;}
}

6、坏键盘打字

链接:【牛客.旧键盘】

import java.util.*;public class Main {public static void func(String str1, String str2){// 1、把str2实际输入的字符放进Set中Set<Character> set = new HashSet<>();for(char ch : str2.toUpperCase().toCharArray()){set.add(ch);}// 2、遍历str1,把已经打印的破坏键盘字符放进setHaveRead,未打印的打印Set<Character> setHaveRead = new HashSet<>();for(char ch : str1.toUpperCase().toCharArray()){// 注意:第一个条件用于过滤未破坏的键盘字符;第二个条件用于过滤破坏了的键盘的已经打印了的字符。//那两者都非,才是破坏键盘又没有打印的字符。if(!set.contains(ch) && !setHaveRead.contains(ch)){setHaveRead.add(ch);System.out.print(ch);}}}public static void main(String[] args) {Scanner in = new Scanner(System.in);String str1 = in.nextLine();String str2 = in.nextLine();func(str1,str2);}
}
  • 使用set来存储未坏键盘的大写字符,然后来处理应输入字符的大写字符。
    具体操作:1、把str2实际输入的字符放进Set中 2、遍历str1,把已经打印的破坏键盘字符放进setHaveRead,未打印的打印

  • 满足条件if(!set.contains(ch) && !setHaveRead.contains(ch)){ setHaveRead.add(ch);时才打印。第一个条件用于过滤未破坏的键盘字符;第二个条件用于过滤破坏了的键盘的已经打印了的字符。那两者都非,才是破坏键盘又没有打印的字符。

  • 另一个思路是写第二个Set未brokenSet,装坏的键盘,让这些字符在brokenSet中自动去重。这种方法在牛客网这道题上过不了,在牛客网这道题中题目默认要求按照字符串从前到后打印破坏的键盘。而Set(TreeSet关于key有序,HashSet无序),并不一定会按照题目从前到后的顺序打印坏的键盘。


http://www.ppmy.cn/news/36721.html

相关文章

美国CPC证书(儿童产品证书)详细解答

儿童产品证书&#xff08;Children’s Product Certificate, CPC&#xff09;适用于所有以12岁及以下儿童为主要目标使用对象的产品&#xff0c;如玩具、摇篮、儿童服装等&#xff0c;如在美国本地生产则由制造商负责提供&#xff0c;如在其他国家生产则由进口商负责提供。也就…

篇章八 Git 详细使用说明

我的github:https://github.com/zhuhukang/gitskills廖雪峰学习文档:https://www.liaoxuefeng.com/wiki/896043488029600/900375748016320中文文档:https://docs.github.com/cn/get-started/quickstart/hello-world图形化学习网址:https://learngitbranching.js.orgGit : …

从数据到智慧,TOOM舆情监测系统让你的决策更加精准!

当今社会信息化程度日益提高&#xff0c;网络平台已成为人们获取最新信息的主要途径&#xff0c;无论是个体还是组织、政府还是企业&#xff0c;都需要通过各种手段及时了解社会舆情&#xff0c;把握市场动态&#xff0c;调整经营策略。而舆情监测系统无疑是这些手段中最为有效…

念一句咒语 AI 就帮我写一个应用,我人麻了...

原文链接&#xff1a;https://forum.laf.run/d/232 作为人类&#xff0c;我们时常会有自己独特的想法和脑洞大开的创意。然而&#xff0c;这些想法往往因为成本过高而无法实现&#xff0c;毕竟每个人的能力和精力都是有限的&#xff0c;尤其是对于程序员而言&#xff0c;不可能…

大数据开发语言Scala(二)——集合的基本属性和常用操作

一、集合简介1.1 集合简介​1&#xff09;Scala的集合有三大类&#xff1a;序列Seq、集Set、映射Map&#xff0c;所有的集合都扩展自Iterable特质。2&#xff09;对于几乎所有的集合类&#xff0c;Scala都同时提供了可变和不可变的版本&#xff0c;分别位于以下两个包不可变集合…

.NET CORE 3.1 继承JWT鉴权和授权

官网&#xff1a;JSON Web Tokens - jwt.ioJSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).…

线程安全、线程同步(同步代码块、同步方法、同步锁)

一. 线程安全 1.1 线程安全问题是什么&#xff0c;发生的原因 多个线程同时修改同一共享资源的时候&#xff0c;会出现线程安全问题。读数据是绝对不会出现线程安全问题的&#xff0c;它一定是因为同时在修改。一旦线程同步了&#xff0c;就是解决了安全问题了。CPU负责调度线…

AD360自助式密码管理

通常情况下&#xff0c;数字企业的 IT 管理员发现自己淹没在与密码相关的票证或与身份相关的问题的海洋中。通过为最终用户部署自助服务解决方案&#xff0c;可以轻松对此进行排序&#xff0c;同时还可以节省组织在解决密码相关问题上花费的时间和金钱。 AD360有助于通过自助密…