本文主要内容:
- Set:HashSet、TreeSet
- Map:HashMap、TreeMap
- Set及Map的应用
1.找出只出现一次的元素
2.复制带随机指针的链表
3.宝石与石头
4.坏键盘打字,找出坏的键
5.前k个高频单词
6.子域名访问计数
集合:Collection(以value的形式存在):List 、Queue 、Set 、 Deque;Map(以key-value的形式存在)
- Set:无序、无重复
**无序:**添加的顺序与获取的顺序不一致(不是集合本身是否有序,Tree自然有序)
**无重复:**添加的元素不能一致(如果出现重复的元素,只存第一个,不在存入)
HashSet(HashMap—>数据存储结构:散列表)
TreeSet(TreeMap—>数据存储结构:二叉树)
Set集合家族基本使用:
add(E e)、remove(E e)、 contains:是否包含
Iterator迭代器(增强for)、 size();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//Set源码中的add方法,实际上用的是Map中的put方法,且将它当做key传入
//所以set无重复,map中的key是无重复的
Set集合无重复的特性:
**HashSet:**无重复的原则有两个方法同时起作用:equals、hashCode,默认比较的是两个对象的地址,若第二个对象地址与之前的一致,不在存入,如果想要改变其比较的规则,可以重写上述的两个方法。
**TreeSet:**无重复的原则只有一个方法起作用:compareTo,上述的方法不是每个对象都有的,若想将某一个对象存入TreeSet集合中,需要让对象所属的类的接口Comparable,实现接口后将ComparatorTo方法重写,返回int,负数靠前排布,正数排列靠后。(有序)
- Map:映射–>通过一个key可以直接定位到一个value值
存储方式:以键值对的形式存储(key-value)
注意:key:无序无重复 value:无序可重复
key无序还是一样,指存入顺序与取得顺序不一致,key无重复指的是元素不能一致
基本方法:增删改查
**增:**put(key,value):存放一组映射关系(key-value)
(1)key添加的顺序与取得的顺序不同
(2)不同的key可以存储相同的value
(3)如果key若是相同,则将原有的value覆盖而不是拒绝存入(跟set刚好相反)
**删:**remove(key);
**改:**put(key,value1); put(key,value2);通过覆盖的形式进行修改
replace(key,newValue)
**查:**get(key)
**遍历集合:**获取到所有的key,遍历key,通过key获取value
其他API中提供的方法:
clear()、containsKey(key)、containsValue(value)、getOrDefault(key,defaultValue):如果key存在就返回对应的value,若没有找到,则返回默认值。
isEmpty()、putAll(map)、putIfAbsent(key,value):如果key不存在才向集合中添加元素,如果key存在就不添加了
HashMap:数据存储方式:散列表(数组+链表)
//计算哈希码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
注意:不同的对象可以产生相同的hashCode码,不同的hashCode码应该对应不同的对象,数组里存放的是对象的HansCode码,如果发现hashCode码一致,就在当前数组下标后面串一个Node。数组存放的是Hash值,将Node{key+value}包装成一个对象Entry:Map.Entry
TreeMap:数据存储方式:二叉树(红黑树)
**自然有序:**按照UniCode编码自然有序,map集合中的key需要可比较的,key的对象需要实现Comparable接口。在构建树时,每一次的对象与之前的entry作比较(compareTo),如果小于就放左边,如果大于就放右边,如果一致就覆盖,注意:红黑二叉树层级多余两层,左旋、右旋
- 在做题时,应该采用数组还是集合呢?
1.数组or集合:如果存储的元素长度不变就用数组,如果长度不确定就用集合
2.长度不确定——集合
List:存储有顺序
ArrayList:更适合遍历轮询
LinkedList:更适合插入删除
Stack:先进后出
Set家族无重复:存储的元素希望自动去掉重复的元素
Hash:性能更高
Tree:希望进去的元素自动去重复,同时还能自动排序
Map家族k-v:通过唯一的k快速寻找v
Hash:性能更高
Tree:希望存进去的元素key自动排序
- 代码实现应用题
class Node{
int value;
Node next;
Node random;
public Node(int value){
this.value = value;
}
}
public class Solution {
//数组中只有一个元素出现了一次,其余元素出现了两次,找出那个只出现一次的元素
public static int singleNumber(int[] number) {
//首先定义一个map记录元素与其出现的次数,然后通过次数找到那个出现一次的元素
Map<Integer,Integer> map = new HashMap<>();
for(int num:number){
int count = map.getOrDefault(num,0);
map.put(num,count+1);
}
int keyResult = 0;
for(Map.Entry<Integer,Integer> entry: map.entrySet()){
int key = entry.getKey();
int value = entry.getValue();
if(value==1){
keyResult = key;
}
}
return keyResult;
}
public static Node createNode(){
Node n1 = new Node(7);
Node n2 = new Node(3);
Node n3 = new Node(5);
Node n4 = new Node(2);
n1.next = n2;n2.next = n3;n3.next = n4;n4.next = null;
n1.random = n3;n2.random = n1;n3.random = n3;n4.random = null;
return n1;
}
public static void show(Node head){
Node cur = head;
while (cur!=null){
System.out.format("%d-->",cur.value);
cur = cur.next;
}
System.out.println("null");
}
//复制带随机指针的链表
public static Node copyRandomList1(Node head) {
//首先将每个结点都复制一份,插入到链表中每个结点的后面,构成一条新链表,通过原链表的random的next找到新链表的random,最后拆分链表
if(head==null){
return null;
}
Node cur = head;
while (cur!=null){
Node node = new Node(cur.value);
node.next = cur.next;
cur.next = node;
cur = cur.next.next;
}
cur = head;
Node newNode = cur.next;
while (cur!=null){
if(cur.random == null){
newNode.random = null;
}else {
newNode.random = cur.random.next;
}
cur = cur.next.next;
}
Node result = head.next;
cur = head;
while (cur!=null){
cur.next = newNode.next;
if(newNode.next!=null){
newNode.next = newNode.next.next;
}
cur = cur.next;
}
return result;
}
public static Node copyRandomList2(Node head) {
//首先将原有的链表复制(除random外)一份,得到一个与原先链表(无random)一样的链表,然后将两个链表的值分别放入map中,新链表的random是旧链表random的value
Map<Node,Node> map = new HashMap<>();
Node cur = head;
Node result = null;
Node last = null;
while (cur!=null){
Node node = new Node(cur.value);
if(result==null){
result = node;
}else {
last.next = node;
}
last = node;
map.put(cur,node);
cur = cur.next;
}
cur = head;
Node n = result;
while (cur!=null){
//cur的random的值就是n的random
n.random = map.get(cur.random);
cur = cur.next;
n = n.next;
}
return result;
}
//J代表宝石类型,S代表你拥有的石头,S中每个字符代表一种石头,返回S中有多少种宝石
public static int numJewelsInStones(String J, String S) {
//将J中的元素放入set中(不重复),然后遍历S,看set中是否包含S中的元素,如果有,count加一
//数组a为宝石,数组b为石头
char[] a = S.toCharArray();
Set<Character> set = new HashSet<>();
for(char ch:a){
set.add(ch);
}
int count = 0;
char[] b = J.toCharArray();
for(char ch:b){
if(set.contains(ch)){
count++;
}
}
return count;
}
public static void main(String[] args) {
int[] number = {2,1,2,1,3,3,4,5,5,6,6};
int keyResult = singleNumber(number);
System.out.println(keyResult);
System.out.println("===============================");
Node node1 = createNode();
copyRandomList1(node1);
show(node1);
System.out.println("===============================");
Scanner scanner = new Scanner(System.in);
String J = scanner.nextLine();
String S = scanner.nextLine();
int baoShiResult = numJewelsInStones(J,S);
System.out.println(baoShiResult);
System.out.println("===============================");
//键盘中有一些键坏了,已知输入的数据以及要输进去的数据,进行对比,将坏掉的键以顺序且大写打印出来
Scanner sc = new Scanner(System.in);
String actual = sc.nextLine();
String now = sc.nextLine();
//now数组且元素都大写
char[] a = now.toUpperCase().toCharArray();
Set<Character> set = new HashSet<>();
for(char ch:a){
set.add(ch);
}
//actual数组且数组大写
char[] b = actual.toUpperCase().toCharArray();
Set<Character> brokenKeys = new HashSet<>();
for(char ch:b){
if(!set.contains(ch)){
if(!brokenKeys.contains(ch)){
System.out.print(ch+" ");
brokenKeys.add(ch);
}
}
}
}
}
//运行结果及截屏
4
===============================
7-->3-->3-->5-->5-->2-->2-->null
===============================
aAb
AAABBBa
2
===============================
String_beatiful_wanMei
Strng_beatful_wanMe
I
复制带随机指针的链表运行结果截屏:
//前k个高频单词
public class Solution1 {
public static class stringComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
public static Map<String, Integer> count(String[] words) {
//首先定义一个map,里面数组中的元素及它要出现的次数
Map<String,Integer> map = new HashMap<>();
for(String s: words){
int count = map.getOrDefault(s,0);
map.put(s,count+1);
}
return map;
}
public static Map<Integer, List<String>> remap(Map<String, Integer> wordToCount) {
//在定义一个remap,里面包含出现相同的次数以及线性表(线性表中装的是出现次数相同的元素)
Map<Integer,List<String>> remap = new HashMap<>();
for(Map.Entry<String,Integer> entry:wordToCount.entrySet()){
String keys = entry.getKey();
int values = entry.getValue();
List<String> list = remap.get(values);
if(list==null){
list = new ArrayList<>();
remap.put(values,list);
}
list.add(keys);
}
return remap;
}
public static List<String> topKFrequent(String[] words, int k) {
//将remap的key放入到一个数组中去,然后对其排序,这样就可以得到次数最高的
Map<String,Integer> wordToCount = count(words);
Map<Integer,List<String>> countToList = remap(wordToCount);
Set<Integer> counts = countToList.keySet();
int[] count = new int[counts.size()];
int i =0;
for(int key:counts){
count[i++] = key;
}
Arrays.sort(count);
//首先定义一个list放入结果元素,定义j=0,当j<k时,数组从后往前取元素,通过remap得到此次数的集合,然后对其进行排序
//判断k-j与集合size作比较,如果小于,则全部加到list中,如果大于截取list后,将元素添加到list中,最后返回list。
List<String> result = new ArrayList<>();
Comparator<String> comparator = new stringComparator();
int j = 0;
int index = count.length-1;
while (j<k){
int c = count[index--];
List<String> wordList = countToList.get(c);
wordList.sort(comparator);
if(wordList.size()<=k-j){
result.addAll(wordList);
j+=wordList.size();
}else {
result.addAll(wordList.subList(0,k-j));
j = k;
}
}
return result;
}
public static void main(String[] args) {
String[] words = {
"i", "love", "leetcode",
"i", "love", "coding"
};
List<String> r = topKFrequent(words, 3);
System.out.println(r);
}
}
//运行结果
[i, love, coding]
//子域名访问计数
public class Solution3 {
//900 google.mail.com-->返回结果900 google.mail.com,900 mail.com,900 com
public static List<String> subDoMainVisits(String[] cpDomMains) {
//首先遍历数组,将所有的元素以空格分开,然后将后面的元素以点分开,接着遍历数组,利用拷贝数组将其组合起来
//定义一个map,里面包含s与次数,最后遍历map,将元素添加到list中
Map<String,Integer> map = new HashMap<>();
List<String> list = new ArrayList<>();
for(String s:cpDomMains){
String[] a = s.split(" ");
int number = Integer.parseInt(a[0]);//将字符串转换为int类型
String[] b =a[1].split("\\.");
for(int i = 0;i<b.length;i++){
String[] c = Arrays.copyOfRange(b,i,b.length);//注意截取时
String result = String.join(".",c);
int numbers = map.getOrDefault(result,0);
map.put(result,numbers+number);
}
}
for(Map.Entry<String,Integer> entry:map.entrySet()){
String keys = entry.getKey();
int values = entry.getValue();
list.add(values+" "+keys);
}
return list;
}
public static void main(String[] args) {
String[] s = { "900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org" };
List<String> r = subDoMainVisits(s);
System.out.println(r);
}
}
//运行结果
[951 com, 900 google.mail.com, 1 intel.mail.com, 5 org, 5 wiki.org, 901 mail.com, 50 yahoo.com]