Java-数据结构-Map和Set(三)-习题 o(´^`)o

目录

❄️一、习题一(只出现一次的数字):

❄️二、习题二(随机链表的复制):

❄️三、习题三(宝石与石头):

 ❄️四、习题四(旧键盘):

 ❄️五、习题五(前k个高频单词):

 ❄️总结:


❄️一、习题一(只出现一次的数字):

         ☑ 题的传送门:

                      只出现一次的数字


这道题呢,有两种快速的解法,其一就是用 ^ 运算,其二就是使用 HashSet 来计算,我们一一来看,这些的解法:

其一:(^解法)

      我们呢在  相同的数字 ^  的时候呢就会为 0 ,所以我们的数字在 ^ 的时候呢,最后的出的数字就是我们的出现一次的数字,比如 1^1^2 = 2

代码:

class Solution {
    public int singleNumber(int[] nums) {
        int ret = nums[0];
        for(int i = 1;i < nums.length; i++) {
            ret = ret ^ nums[i];
        }
        return ret;
    }
}

 其一:(HashSet的解法)

         我们每次检查 HashSet 中呢是否有 nums[i] 这个数值,如果有呢就把这个数值从 HashSet中删除,如果没有这个数值,就把其入 HashSet 中,最后在 HashSet 中的数值就是我们要找的值

代码:

class Solution {
    public int singleNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for(int s : nums) {
            if(set.contains(s)) {
                set.remove(s);
            }else {
                set.add(s);
            }
        }
        for(int i = 0; i < nums.length;i++) {
            if(set.contains(nums[i])) {
                return nums[i];
            }
        }
        return -1;
    }
}

❄️二、习题二(随机链表的复制):

         ☑ 题的传送门:

                      随机链表的复制


    这道题呢不是简单得直接把 链表复制下来就结束的。这道题呢还是比较麻烦的,这个需要深拷贝的,这里呢,我们使用 HashMap 来做。

          因为呢 我们这道题的 random 可能指向的是自己也可能是空,也可能是 跳跃的指向。而且我们拷贝之后呢,我们新的节点是一个 新的地址 ,这样呢我们直接拷贝的话,我们的 next 和 random 这个就不是指向我们下一个节点了。

步骤: 

1、我们先把链表的节点的值拷贝到 HashMap 中

2、我们的 HashMap 里面存放的是 <Node,Node> 

3、根据我们 HashMap 中拷贝的节点值,再把对应的 next 和 random 拷贝进去。

 

OK,理解这个步骤之后呢,我们来看看代码如何编写的: 

class Solution {
    public Node copyRandomList(Node head) {
       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;
       }

       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);
    }
}

❄️三、习题三(宝石与石头):

         ☑ 题的传送门:

                        宝石与石头


       这个题呢还是比较简单的,我们直接使用 HashSet 就可以快速解决。

步骤:

1、我们先把 “宝石” 中的每一个字符放到 HashSet 中

2、遍历 “石头” 这个字符串,之后查看 HashSet 中是否存在 这个字符,如果存在 count++

是不是非常简单,所以呢我们来看看代码:

class Solution {
    public int numJewelsInStones(String jewels, String stones) {
        HashSet<Character> set = new HashSet<>();
        for(int i = 0;i < jewels.length();i++) {
            //先把宝石类型的字符串都放到HashSet中
            char ch = jewels.charAt(i);
            set.add(ch);
        }
        int count = 0;
        for(int j = 0;j < stones.length();j++) {
            //根据HashSet中的值和石头的字符一一比较,相同的count++
            char ch = stones.charAt(j);
            if(set.contains(ch)) {
                count++;
            }
        }
        return count;
    }
}

OK,这个题就是这个样子的非常的简单的,我们来看看下一道题。


 ❄️四、习题四(旧键盘):

         ☑ 题的传送门:

                      旧键盘


     这个题呢和我们上面的题呢是差不多的思路。

步骤:

1、先把 两个字符串 都转换成大写的

2、之后把 键盘坏的打的字符串 都放到 HashSet 中

3、我们再创建一个 HashSet ,遍历 好的键盘的打出来的字符 

4、如果开始的那个 HashSet 中没有 好的字符 并且 后创建的那个 HashSet 中没有这个字符的话,就将其打印出来,并且放到后面的 HashSet 中。

这个题呢,还是比较好理解的,所以呢我们直接来看代码如何编写的:

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 处理多个 case
            String str1 = in.nextLine();
            String str2 = in.nextLine();
            func(str1,str2);
        }
    }

    public static void func(String str1,String str2) {
        //转成大写
        str1 = str1.toUpperCase();
        str2 = str2.toUpperCase();

        //把坏的键盘的字符串放到 HashSet 中
        HashSet<Character> set1 = new HashSet<>();
        for(int i = 0; i < str2.length();i++) {
            char ch = str2.charAt(i);
            set1.add(ch);
        }

        HashSet<Character> set2 = new HashSet<>();        
        //遍历str1 看看set1 和 set2 中是否 存在
        //如果不存在就放到 set2 中 并且 打印
        for(int i = 0; i < str1.length();i++) {
            char ch = str1.charAt(i);
            if (!set1.contains(ch) && !set2.contains(ch)) {
                set2.add(ch);
                System.out.print(ch);
            }
        }
    }
}

OK,这个就是这个题的代码,上面的那个题麻烦一些。


 ❄️五、习题五(前k个高频单词):

         ☑ 题的传送门:

                       前k个高频单词


     这道题呢就比较难了,看到这个 “前k个” 是不是想起了我们以前介绍过的 Top-k 问题,我们的这道题呢就是存在 Top-k 问题的 并且和 HashMap 联合一起做这道题,才可以,我们来看看如何做到的。

步骤:

1、先定义一个 HashMap 里面的 key-value 是 String-Integer ,我们的 Integer 用来记录对应的String出现了几次。

2、建立 小根堆 这里不是简单的存储 Integer 而是存储 HashMap 的底层的 Map.Entry<String,Integer>,根据这里的Integer 来进行比较的,但是这里呢要注意的是,当不同的单词出现的次数相同的时候呢,我们根据 字典的顺序进行存储,所以这里的 创建小根堆 是要传一个新的比较器的

3、每次去 map 的数据,先把 堆的长度 和 k进行比较:

  如果:堆的长度  < k 的话,就直接入堆

  如果:堆的长度 >= k 的话,我们还需要判断:

             如果:堆顶数据的value值 < map的 value 值的话,说明map对应的 key 出现的次数多,                          就把 堆顶的数据进行出堆,并且把 map的这个数据 入堆。

             如果:堆顶数据的value值 == map的 value 值的话。我们就要判断对应的 key 的大小了

                        如果:堆顶数据的key 值 > map的 key 值的话,我们就把堆顶数据 出堆,并且把                                     map 的数据进行 入堆。

4、我们创建一个 ArrayList 的集合,把 堆中的 k 个 String 都放到 ArrayList 中,这个时候还是存在问题的,这时 ArrayList 中存放的是从小到大的排序,我们要把其 翻转过来。

5、对其 ArrayList 进行翻转。

这个呢就是这道题的大体思路了,我们来看看如何进行编写的代码:

class Solution {
   public List<String> topKFrequent(String[] words, int k) {
        HashMap<String,Integer> map = new HashMap<>();
        //1. 统计每个单词出现的次数
        for(String word : words) {
            if(map.get(word) == null) {
                map.put(word,1);
            }else {
                int val = map.get(word);
                map.put(word,val+1);
            }
        }

        //2. 建立小根堆
        PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(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().compareTo(o2.getValue());
            }
        });
        //3.遍历map
        for(Map.Entry<String,Integer> entry : map.entrySet()) {
            if(minHeap.size() < k) {
                minHeap.offer(entry);
            }else {
                Map.Entry<String,Integer> top = minHeap.peek();
                if(top.getValue().compareTo(entry.getValue()) < 0) {
                    minHeap.poll();
                    minHeap.offer(entry);
                }else if(top.getValue().compareTo(entry.getValue()) == 0) {
                    if(top.getKey().compareTo(entry.getKey()) > 0) {
                        minHeap.poll();
                        minHeap.offer(entry);
                    }
                }
            }
        }
        ArrayList<String> list = new ArrayList<>();

        for (int i = 0; i < k; i++) {
            Map.Entry<String,Integer> tmp = minHeap.poll();
            list.add(tmp.getKey());
        }
        //e-2 b-5 c-6
        Collections.reverse(list);
        return list;
    }
}


 ❄️总结:

    OK,这次关于我们的 哈希表 相关的练习题呢,到这里就结束了,让我们下次再见,下次呢我们就要进入新的知识章节了,让我们尽情期待吧!!!拜拜~~~

1.1 单项选择题 1. 数据结构是一门研究非数值计算的程序设计问题中,数据元素的① 、数据信息在计算机中的② 以及一组相关的运算等的课程。 ① A.操作对象   B.计算方法  C.逻辑结构  D.数据映象 ② A.存储结构 B.关系 C.运算 D.算法 2. 数据结构DS(Data Struct)可以被形式地定义为DS=(D,R),其中D是① 的有限集合,R是D上的② 有限集合。 ① A.算法 B.数据元素 C.数据操作 D.数据对象 ② A.操作 B.映象 C.存储 D.关系 3. 在数据结构中,从逻辑上可以把数据结构分成 。 A.动态结构静态结构 B.紧凑结构非紧凑结构 C.线性结构非线性结构 D.内部结构外部结构 4. 算法分析的目的是① ,算法分析的两个主要方面是② 。 ① A. 找出数据结构的合理性 B. 研究算法中的输入输出的关系 C. 分析算法的效率以求改进 D. 分析算法的易懂性文档性 ② A. 空间复杂性时间复杂性 B. 正确性简明性 C. 可读性文档性 D. 数据复杂性程序复杂性 5. 计算机算法指的是① ,它必具备输入、输出② 等五个特性。 ① A. 计算方法 B. 排序方法 C. 解决问题的有限运算序列 D. 调度方法 ② A. 可行性、可移植性可扩充性 B. 可行性、确定性有穷性 C. 确定性、有穷性稳定性 D. 易读性、稳定性安全性 1.2 填空题(将正确的答案填在相应的空中) 1. 数据逻辑结构包括 、 、 四种类型,树形结构图形结构合称为 。 2. 在线性结构中,第一个结点 前驱结点,其余每个结点有且只有 个前驱结点;最后一个结点 后续结点,其余每个结点有且只有 个后续结点。 3. 在树形结构中,树根结点没有 结点,其余每个结点有且只有 个直接前驱结点,叶子结点没有 结点,其余每个结点的直接后续结点可以 。 4. 在图形结构中,每个结点的前驱结点数后续结点数可以 。 5. 线性结构中元素之间存在 关系,树形结构中元素之间存在 关系,图形结构中元素之间存在 关系。 6. 算法的五个重要特性是__ __ , __ __ , ___ _ , __ __ , _ ___。 7. 分析下面算法(程序段),给出最大语句频度 ,该算法的时间复杂度是__ __。 for (i=0;i<n;i++) for (j=0;j<n; j++) A[i][j]=0; 8. 分析下面算法(程序段),给出最大语句频度 ,该算法的时间复杂度是__ __。 for (i=0;i<n;i++) for (j=0; j<i; j++) A[i][j]=0; 9. 分析下面算法(程序段),给出最大语句频度 ,该算法的时间复杂度是__ __。 s=0; for (i=0;i<n;i++) for (j=0;j<n;j++) for (k=0;k<n;k++) s=s+B[i][j][k]; sum=s; 10. 分析下面算法(程序段)给出最大语句频度 ,该算法的时间复杂度是__ __。 int i=0,s=0; while (s<n) { i++; s+=i; //s=s+i } 11. 分析下面算法(程序段)给出最大语句频度 ,该算法的时间复杂度是__ __。 i=1; while (i<=n) i=i*2;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值