一、map & set 基础练习
1.有十万个数据,找到第一个重复的数据
就是说我们 在这个题上用了 set 的有关性质 , 我们有十万个数据,要查找到第一个重复的数据,我们可以这样: 我们将 list 中的数据一个一个放入 set中,如果 set没有这个数据,那么就 放入set中,如果 set中包含了这个数据,那么打印这个数据,同时 break;
2. 有十万个数据,去除掉所有重复的数据
直接遍历这个数组,将数组所有数据全部放进 set中,重复的数据自然会 插入失败,所以最后 set 中的元素全都是 不重复的数据.
3.有十万个数据,统计每个数据出现了多少次
使用map 和 set 来解题
这道题非常经典,所以我们 一定要理解深刻
我们将 数组中的 各个数据,及数据出现的次数 作为一个键值对 放入Map 中
put之前先判断 map 中之前是否有 key ,如果没有的话,map.getKey() == null ,我们就直接 map.put( key,1 ).
如果没有的话,先 int count = map.getKey(), 得到这个key之前出现的次数,然后 map.put(key,count+1)
最后遍历 map,将map的所有键值对 全部打印出来.
二、 刷题练习
1. 只出现一次的数字
题解代码1:
写这个代码的思路: 我们有一个 Set 的集合,先遍历数组,如果Set 里不包含 nums[i],那么就把 nums[i] 放入 set 中,但是如果 Set 中包含 nums[i] ,那么就把 set中的 nums[i] 删除.
到最后 set中剩下的就是只出现一次 的数字
题解代码2:
写这个思路的代码: 这个是用Map 记录了nums 数组中每一个数据出现的次数,最后遍历 map.entrySet(),当 entry.getVaue()==1 时,这个数据就出现了一次,返回 entry.getKey().
如果在这组数据中没有单独出现的数字,那么返回-1(不要那么严谨,只是了解逻辑即可,不需要联系业务逻辑.)
2. 复制随机指针
比如说我们有这样一个链表,我们要做到的就是 复制这样一组相同结构的 链表
这里有一个注意点,同样也是难点:就是引用的复制
我们在复制节点的时候,不能够全部一块复制,否则就会出现这样的情况
我们发现复制节点的全部信息的话,新节点指向的next 、random 指向的还是原节点的位置
所以 next、random 我们要重新赋值,新节点新链表的结构要像原链表一样…
我们如何解决呢?
我们在 遍历原链表的时候,每走一个节点 cur ,就 new 一个新的节点 node ,原节点和新节点是一一对应的关系,map.put(cur,node ).
我们可以通过这样,来使新的节点组成 原链表的关系
map.get(cur).next = map.get(cur.next) map.get(random).next = map.get(cur.random);
题解代码:
3.宝石与石头
(1)暴力破解法
暴力法的思路很直观,遍历字符串 stones,对于 stones 中的每个字符,遍历一次字符串 jewels,如果其和 jewels 中的某一个字符相同,则是宝石。
(2) Hash集合法
方法一中,对于字符串stones 中的每个字符,都需要遍历一次字符串 jewels,导致时间复杂度较高O(m*n)。如果使用哈希集合存储字符串 jewels 中的宝石,则可以降低判断的时间复杂度。
遍历字符串 jewels,使用哈希集合存储其中的字符,然后遍历字符串 stones,对于其中的每个字符,如果其在哈希集合中,则是宝石。时间复杂度 O(m+n)
题解代码:
4. 坏键盘打字
题解代码:
题解思路:
这个题非常注意 输出的格式. 输出的时候找出的键盘全都是 大写的字母数字
- str 1 ---- 期望输出的字符串
- str 2 — 实际输出的字符串
设置 一个 setAutal 将实际输出的键的大写字符放入到 setAutal 中
先将 str2 的字符转换成为 大写 ,然后 str2 转换成数组 ,foreach 遍历.
将大写的 str2 字符放入到 setAutal 集合中.
设置一个 setBroken 将坏的键 放入到 这个集合中
怎么判断这是一个坏的键呢?
遍历 str1 期望输出的字符串(记得直接大写遍历),如果 setAutal 不包含 str1 中的键,把那个键 存入到 setBroken.
打印的时候 有几点注意: 他打印的规则 ,一定是遍历期望打印的数组,一个一个字符遍历,只要 期望打印的字符 在 setAutal 中没有的话,那么先放进 setBroken ,然后 打印 这个字符 ch,我们为什么要放进 setBroken 呢? 因为打印之前还有一个条件,就是 setBroken 中已经有的就不打印了.否则就会出现 这个坏的键 重复打印.
5.前 k 个高频单词
题目描述:
题解代码:
这道题可以说的上是 以前做leetcode 以来代码量最多的一道题了,我先说一句没那么简单,但也是有 基本的 topK问题变形而来的.
我先说写这个题的逐步思路吧…
1.首先这个是一个 topK 问题,要求我们把 出现次数最多的 k 个数据 输出,,我们已经学过了 map,将他给我们提供的 字符串数组进行遍历,得到每个数据 与其对应的 出现的次数
2.接下来是topK 的思路,找到 次数最多的k个数据,那我们就要建立一个 大小为k 的小堆
3.往堆中 放入 map 中的数据
当 大于 k 时,往堆中放数据 ,要注意: 当遍历元素 出现次数 与 堆顶元素出现次数 相同时 ,比较字符串,字符串小的优先入队.
4.因为返回的是 List ,所以用一个 list 来接收堆中的数据
5.我们来看一下 测试通过的情况.
我们发现 解答错误,具体来看一下,发现有一个问题没有解决, 我们只在 当 k+1 遍历元素的时候提供了 当出现次数相等时比较字符串大小的思路, 但是我们在 当 minheap.siez()<k ,没有提供 这个,所以在我们建小堆的时候 应该处理一下这种情况
6.处理 当 size<k 时建堆的情况
7.我们再来看一下 ,测试的情况
又出现了解答错误,我们发现他想要我们输出的是从大到小的 出现次数的结果,但是我们建立的小堆,每次弹出最小的放入 list 中,这是从小到大的结果,为了解决这个输入问题,我们呢可以逆置顺序表.
8.逆置顺序
9.再来测试我们的结果
发现又是解答错误,这是为什么呢?
之前在没有逆置之前,我们已经解决了 这个 k < minHeap.size() 的问题,现在逆置了一下,又把我们正确的结果给颠倒了,所以现在 我们将之前 建堆时 出现次数相等
return o1.getKey().compareTo(o2.getKey());
改为----
return o2.getKey().compareTo(o1.getKey())
这样就恢复到正确的顺序了.
10.最后完整版测试情况.
最后成功通过…
6.下厨房
题目描述:
我们先 理清一下这道题目 想要表达什么意思,这道题就是牛牛 输入的每一行是他想要 做的一道菜所需要的 材料,注意: 菜 与 菜 之间的材料 很可能会重复 ,现在要 求一共有多少种材料
思路:
1.首先肯定要用 set 集合遍历每一种 材料,来存储 这些不同的材料,相当于 去重了.
2.这是多行输入
3.我们要把他输入的每一行的 字符串 以空格为 分隔符 ,把每一个材料 专门 放在 一个数组中. 要用到 字符串的分割函数 split
7.斐波那契数列
1.题目描述:
2.思路:
(1) 构建斐波那契数列,同时输入一个 n
(2)写一个循环 ,目的是找到 n 的左右两边 的斐波那契数 , 循环的条件就是 f2<n
当循环跳出来时 ,f2 >= n , f1 必定 小于 n ,所以
(3) 此时 f2 >= n , f1 必定 小于 n ,我们要计算 n 到斐波那契数需要最少的步数
比较 f1-n 和 f2-n 的绝对值,可以得到 n 距离 f1、f2 哪一个最近
谁比较小,就打印谁的绝对值
代码展示:
今天的分享到这里就结束了,感谢大家的欣赏!!
|