赞
踩
目录
方法
|
解释
|
K getKey()
|
返回
entry
中的
key
|
V getValue()
|
返回
entry
中的
value
|
V setValue(V value)
|
将键值对中的
value
替换为指定
value
|
3.Map 的常用方法说明
方法
|
解释
|
V get(Object key)
|
返回
key
对应的
value
|
V getOrDefault(Object key, V defaultValue)
|
返回
key
对应的
value
,
key
不存在,返回默认值
|
V put(K key, V value)
|
设置
key
对应的
value
|
V remove(Object key)
|
删除
key
对应的映射关系
|
Set<K> keySet()
|
返回所有
key
的不重复集合
|
Collection<V> values()
|
返回所有
value
的可重复集合
|
Set<Map.Entry<K, V>> entrySet()
|
返回所有的
key-value
映射关系
|
boolean containsKey(Object key)
|
判断是否包含
key
|
boolean containsValue(Object value)
|
判断是否包含
value
|
Map
底层结构
|
TreeMap
|
HashMap
|
底层结构
| 红黑树 |
哈希桶
|
插入
/
删除
/
查找时间复杂度
| O(logN) |
O(1)
|
是否有序
|
关于
Key
有序
|
无序
|
线程安全
|
不安全
|
不安全
|
插入
/
删除
/
查找区别
|
需要进行元素比较
|
通过哈希函数计算哈希地址
|
比较与覆写
|
key
必须能够比较,否则会抛出ClassCastException异常
|
自定义类型需要覆写
equals
和hashCode方法
|
应用场景
|
需要
Key
有序场景下
|
Key
是否有序不关心,需要更高的时间性能
|
方法
|
解释
|
boolean add(E e)
|
添加元素,但重复元素不会被添加成功
|
void clear()
|
清空集合
|
boolean contains(Object o)
|
判断
o
是否在集合中
|
Iterator<E> iterator()
|
返回迭代器
|
boolean remove(Object o)
|
删除集合中的
o
|
int size()
|
返回
set
中元素的个数
|
boolean isEmpty()
|
检测
set
是否为空,空返回
true
,否则返回
false
|
Object[] toArray()
|
将
set
中的元素转换为数组返回
|
boolean containsAll(Collection<?> c)
|
集合
c
中的元素是否在
set
中全部存在,是返回
true
,否则返回false
|
boolean addAll(Collection<? extends E> c)
|
将集合
c
中的元素添加到
set
中,可以达到去重的效果
|
Set
底层结构
|
TreeSet
|
HashSet
|
底层结构
|
红黑树
|
哈希桶
|
插入
/
删除
/
查找时间复杂度
| O(logN) |
O(1)
|
是否有序
|
关于
Key
有序
|
不一定有序
|
线程安全
|
不安全
|
不安全
|
插入
/
删除
/
查找区别
|
按照红黑树的特性来进行插入和删除
|
①先计算
key
哈希地址②
然后进行插入和删除
|
比较与覆写
|
key
必须能够比较,否则会抛出ClassCastException异常
|
自定义类型需要覆写
equals
和hashCode方法
|
应用场景
|
需要
Key
有序场景下
|
Key
是否有序不关心,需要更高的时间性能
|
1.给定10W个数据,统计每个数据出现的次数
- public class TestDemo {
- public static Map<Integer,Integer> func(int[] arr){
- Map<Integer,Integer> map = new HashMap<>();
- for (int x:arr) {
- if(map.get(x) == null){
- map.put(x,1);
- }else{
- int val = map.get(x);
- map.put(x,val+1);
- }
- }
- return map;
- }
- public static void main(String[] args) {
- int[] arr = new int[10_0000];
- Random random = new Random();
- for (int i = 0; i < arr.length; i++) {
- arr[i] = random.nextInt(1000);
- }
- Map<Integer,Integer> map = func(arr);
- System.out.println(map);
- }
- }
2.将10W个数据中的数据去重
把数据放到Set即可
- public static Set<Integer> func2(int[] arr){
- HashSet<Integer> set = new HashSet<>();
- for (int x:arr) {
- set.add(x);
- }
- return set;
- }
3.从10W个数据中找到第一个重复的数据
- public static int func3(int[] arr){
- HashSet<Integer> set = new HashSet<>();
- for (int x:arr) {
- if(set.contains(x)){
- return x;
- }
- set.add(x);
- }
- return -1;
- }
- class Node{
- public int val;
- public Node left;
- public Node right;
-
- public Node(int val){
- this.val = val;
- }
- }
- public class BinarySearchTree {
- Node root = null;
-
- public Node search(int key){
- Node cur = root;
- while(cur != null){
- if(cur.val < key){
- cur = cur.right;
- }else if(cur.val > key){
- cur = cur.left;
- }else{
- return cur;
- }
- }
- return null;//没有这个数据
- }
- }
- class Node{
- public int val;
- public Node left;
- public Node right;
-
- public Node(int val){
- this.val = val;
- }
- }
- public class BinarySearchTree {
- Node root = null;
-
- public boolean insert(int val){
- if(root == null){
- root = new Node(val);
- return true;
- }
- Node cur = root;
- Node parent = null;
- while(cur != null){
- if(cur.val < val){
- parent = cur;
- cur = cur.right;
- }else if(cur.val == val){
- return false;//不能有相同数据进行插入
- }else{
- parent = cur;
- cur = cur.left;
- }
- }
- Node node = new Node(val);
- if(parent.val < val){
- parent.right = node;
- }else{
- parent.left = node;
- }
- return true;
- }
-
- }
- class Node{
- public int val;
- public Node left;
- public Node right;
-
- public Node(int val){
- this.val = val;
- }
- }
- public class BinarySearchTree {
- Node root = null;
-
- public void remove(int key){
- Node cur = root;
- Node parent = null;
- while(cur != null){
- if(cur.val == key){
- removeNode(cur,parent);
- break;
- }else if(cur.val < key){
- parent = cur;
- cur = cur.right;
- }else{
- parent = cur;
- cur = cur.left;
- }
- }
- }
- public void removeNode(Node cur,Node parent){
- if(cur.left == null){
- if(cur == root){
- root = cur.right;
- }else if(cur == parent.left){
- parent.left = cur.right;
- }else{
- parent.right = cur.right;
- }
- }else if(cur.right == null){
- if(cur == root){
- root = cur.left;
- }else if(cur == parent.left){
- parent.left = cur.left;
- }else{
- parent.right = cur.left;
- }
- }else{
- Node targetParent = cur;
- Node target = cur.right;
- while(target.left != null){
- targetParent = target;
- target = target.left;
- }
- cur.val = target.val;
- if(target == targetParent.left){
- targetParent.left = target.right;
- }else {
- targetParent.right = target.right;
- }
- }
- }
- public void inOrder(Node root){
- if(root == null){
- return;
- }
- inOrder(root.left);
- System.out.print(root.val + " ");
- inOrder(root.right);
- }
- }
①哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
②哈希函数计算出来的地址能均匀分布在整个空间中
③哈希函数应该比较简单
常见的哈希函数:
①直接定制法(常用):取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道关键字的分布情况 使用场景:适合查找比较小且连续的情况
我们来看一条题:
代码实现:
- class Solution {
- public int firstUniqChar(String s) {
- if(s == null){
- return -1;
- }
- int[] arr = new int[26];
- for(int i = 0; i < s.length(); i++){
- char ch = s.charAt(i);
- arr[ch-97]++;
- }
- for(int i = 0; i < s.length(); i++){
- char ch = s.charAt(i);
- if(arr[ch-97] == 1){
- return i;
- }
- }
- return -1;
- }
- }
研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。 因此:比散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷
(ii)开散列
代码实现:
- public class HashBuck {
- static class Node{
- public int key;
- public int val;
- public Node next;
-
- public Node(int key, int val){
- this.key = key;
- this.val = val;
- }
- }
- public Node[] arr;
- public int usedSize;
- public static final double DEFAULT_LOADFactor = 0.75;
-
- public HashBuck(){
- this.arr = new Node[10];
- }
-
- public void put(int key, int val){
- //找到key所在的位置
- int index = key % this.arr.length;
- //遍历下标的链表,看看是不是有相同的key,如果有,要更新val
- Node cur = arr[index];
- while(cur != null){
- if(cur.key == key){
- cur.val = val;
- return;
- }
- cur = cur.next;
- }
- //没有key这个元素,头插法插入
- Node node = new Node(key,val);
- node.next = arr[index];
- arr[index] = node;
- this.usedSize++;
- //插入元素成功后,检查当前散列表的负载因子
- if(loadFactor() > DEFAULT_LOADFactor){
- resize();//扩容----->不是简单的把数组扩大,数组里面的每个链表的每个元素必须重新进行哈希
- //14%10=4 14%20=14
- }
- }
-
- private void resize(){
- Node[] newArr = new Node[arr.length*2];
- for(int i = 0; i < this.arr.length; i++){
- Node cur = arr[i];
- while(cur != null){
- int index = cur.key % newArr.length;//获取新的下标
- //把cur这个节点,以头插或者尾插的方式插入到新的数组对应下标的链表当中
- Node curNext = cur.next;
- cur.next = newArr[index];//先绑定前面
- newArr[index] = cur;//再绑定后面
- cur = curNext;
- }
- }
- arr = newArr;
- }
-
- private double loadFactor(){
- return 1.0 * usedSize / this.arr.length;
- }
-
- public int get(int key){
- //找到key所在的位置
- int index = key % this.arr.length;
- //遍历下标的链表,看看是不是有相同的key,如果有,要更新val
- Node cur = arr[index];
- while(cur != null){
- if(cur.key == key){
- return cur.key;
- }
- cur = cur.next;
- }
- return -1;
- }
- }
- import java.util.HashMap;
- import java.util.Objects;
-
- class Person{
- public String ID;
-
- public Person(String ID){
- this.ID = ID;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Person person = (Person) o;
- return Objects.equals(ID, person.ID);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(ID);
- }
-
- @Override
- public String toString() {
- return "Person{" +
- "ID='" + ID + '\'' +
- '}';
- }
- }
- public class HashBuck2<K,V> {
- static class Node<K,V>{
- public K key;
- public V val;
- public Node<K,V> next;
-
- public Node(K key, V val){
- this.key = key;
- this.val = val;
- }
- }
- public Node<K,V>[] arr = (Node<K,V>[])new Node[10];
- public int usedSize;
-
- public void put(K key, V val){
- int hash = key.hashCode();//hashcode()寻找位置
- int index = hash % arr.length;
- Node<K,V> cur = arr[index];
- while(cur != null){
- if(cur.key.equals(key)){//equals()负责查看有没有一样的元素
- cur.val = val;
- return;
- }
- cur = cur.next;
- }
- Node<K,V> node = new Node(key,val);
- node.next = arr[index];
- arr[index] = node;
- this.usedSize++;
- }
- //hashcode()一样,equals()不一定一样;equals()一样,hashcode()一定一样
-
- public V get(K key){
- int hash = key.hashCode();//hashcode()寻找位置
- int index = hash % arr.length;
- Node<K,V> cur = arr[index];
- while(cur != null){
- if(cur.key.equals(key)){//equals()负责查看有没有一样的元素
- return cur.val;
- }
- cur = cur.next;
- }
- return null;
- }
-
- public static void main(String[] args) {
- Person person1 = new Person("123");
- Person person2 = new Person("123");
-
- HashBuck2<Person,String> hashBuck2 = new HashBuck2();
- hashBuck2.put(person1,"abc");
-
- System.out.println(hashBuck2.get(person2));
-
- }
- }
编译运行该代码,输出如下:
abc
(4)一个小题
求下面的查找成功的平均长度以及查找不成功的平均长度:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。