【数据结构】Map和Set

目录

1.前言

2.搜索树

2.1概念

2.2操作-查找

2.3操作-插入

2.4操作-删除

2.5性能分析

3.搜索

3.1概念及场景

3.2模型

3.3Map 的使用

3.3.1关于Map的说明

3.3.2关于Map.Entry的说明,>

3.3.3Map 的常用方法说明

3.3.4TreeMap的使用

3.4Set 的使用

3.4.1Set 的说明

3.4.2Set的常用方法说明

3.4.3TreeSet的使用

4.总结


1.前言

我们上次介绍了数据结构中的7中常见的排序算法,今天要给大家分享数据结构路上的一个新征程——Map和Set,让我们一起接着往下了解并学习。

2.搜索树

2.1概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树 :
  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

比如{5,3,4,1,7,8,2,6,0,9},画成二叉搜索树如下所示:

我们首先构建二叉搜索树。

public class BinarySearchTree {
    static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int val) {
            this.val = val;
        }
    }
}

 2.2操作-插入

1. 如果 树为空树 ,即 根 == null ,直接插入。

2. 如果树不是空树,按照查找逻辑确定插入位置,插入新结点。

    //插入
    public void insert(int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            return;
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            }else {
                return;
            }
        }
        //parent 指向的节点 就是需要插入的节点位置 父节点
        if(parent.val >val){
            parent.left =node;
        }else {
            parent.right =node;
        }
    }

2.3操作-查找

    //查询
    public TreeNode root = null;

    public TreeNode search(int k) {
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val > k) {
                cur = cur.right;
            } else if (cur.val < k) {
                cur = cur.left;
            } else {
                return cur;
            }
        }
        return null;
    }

2.4操作-删除

设待删除结点为 cur, 待删除结点的双亲结点为 parent。
1. cur.left == null
2. cur.right == null
3. cur.left != null && cur.right != null
Tips:需要使用 替换法 进行删除,即在它的右子树中寻找中序下的第一个结点 ( 关键码最小 ),用它的值填补到被删除节点中,再来处理该结点的删除问题。
    //删除
    public void remove(int key) {
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < key) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > key) {
                parent = cur;
                cur = cur.left;
            } else {
                removeNode(parent, cur);
                return;
            }
        }
    }

    private void removeNode(TreeNode parent, TreeNode cur) {
        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;
            }
            if (cur == parent.left) {
                parent.left = cur.left;
            } else {
                parent.right = cur.left;
            }
        } else {
            TreeNode target = cur.right;
            TreeNode targetP = cur;
            while (target.left != null) {
                targetP = target;
                target = target.left;
            }
            cur.val = target.val;
            if (target == target.left) {
                targetP.left = target.right;
            } else {
                targetP.right = target.right;
            }
        }
    }

2.5性能分析

  • 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
  • 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
  • 但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树。

 下面举个例子如{6,4,3,5,8,7,9}

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log₂N

最差情况下,二叉搜索树退化为单支树,其平均比较次数为: N/2

3.搜索

3.1概念及场景

Map set 是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关 。以前常见的搜索方式有:
1. 直接遍历,时间复杂度为 O(N) ,元素如果比较多效率会非常慢;
2. 二分查找,时间复杂度为 O(log₂N), , 但搜索前必须要求序列是有序的。

上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:

1. 根据姓名查询考试成绩

2. 通讯录,即根据姓名查询联系方式

3. 不重复集合,即需要先搜索关键字是否已经在集合中

可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本次介绍的MapSet是一种适合动态查找的集合容器。

3.2模型

一般把搜索的数据称为关键字( Key ,和关键字对应的称为值( Value ,将其称之为 Key-value 的键值对,所以模型会有两种:
1. key 模型 ,比如:
  • 有一个英文词典,快速查找一个单词是否在词典中
  • 快速查找某个名字在不在通讯录中
2. Key-Value 模型 ,比如:
  • 统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>
  • 梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

 Map中存储的就是key-value的键值对,Set中只存储了Key

3.3Map 的使用

 具体可以参考[Map的官方文档]:

3.3.1关于Map的说明

Map 是一个接口类,该类没有继承自 Collection ,该类中存储的是 结构的键值对,并且 K 一定是唯一的,不 能重复

3.3.2关于Map.Entry<K, V>的说明

Map.Entry<K, V> Map内部实现的用来存放键值对映射关系的内部类,该内部类中主要提供了<key, value> 的获取,value的设置以及Key的比较方式。

方法
解释
K getKey()返回 entry 中的 key
V getValue()返回 entry 中的 value
V setValue(V value)将键值对中的value替换为指定value

注意:Map.Entry<K,V>并没有提供设置Key的方法。

3.3.3Map 的常用方法说明

方法解释
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 values()返回所有 value 的可重复集合
Set<Map.Entry<K, V>> entrySet()返回所有的 key-value 映射关系
boolean containsKey(Object key)判断是否包含 key
boolean containsValue(Object value)判断是否包含 value

注意: 

1. Map 是一个接口,不能直接实例化对象 ,如果 要实例化对象只能实例化其实现类 TreeMap 或者 HashMap
2. Map 中存放键值对的 Key 是唯一的, value 是可以重复的
3. TreeMap 中插入键值对时, key 不能为空,否则就会抛 NullPointerException 异常 value可以为空。但是 HashMap key value 都可以为空。
4. Map 中的 Key 可以全部分离出来,存储到 Set 来进行访问 ( 因为 Key 不能重复 )
5. Map 中的 value 可以全部分离出来,存储在 Collection 的任何一个子集合中 (value 可能有重复 )
6. Map 中键值对的 Key 不能直接修改, value 可以修改,如果要修改 key,只能先将该 key删除掉,然后再来进行重新插入
7. TreeMap HashMap 的区别
Map底层结构TreeMapHashMap
底层结构
红黑树
哈希桶
插入/删除/查找时间复杂度O(log₂N)O(1)
是否有序
关于Key有序无序
线程安全
不安全不安全
插入 / 删除 / 查找区别
需要进行元素比较通过哈希函数计算哈希地址
比较与覆写
key必须能够比较,否则会抛出 ClassCastException异常自定义类型需要覆写equals和 hashCode方法
应用场景
需要Key有序场景下不关心Key是否有序,需要更高的时间性能

3.3.4TreeMap的使用

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Test {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();
        //put(K key, V value)  设置 key 对应的 value
        map.put("小米手环9", 249);
        map.put("红米K60", 3299);

        get(Object key) 返回 key 对应的 value
        System.out.println("小米手环9:" + map.get("小米手环9"));
        System.out.println("红米K60:" + map.get("红米K60"));

        //getOrDefault(Object key, V defaultValue) 返回 key 对应的 value,key 不存在,返回默认值
        System.out.println(map.getOrDefault("小米", 9999));

        //Set<K> keySet()  返回所有 key 的不重复集合
        //把所有key放在了集合Set当中
        Set<String> set = map.keySet();
        System.out.println(set);

        //containsKey(Object key) 判断是否包含 key
        System.out.println(map.containsKey("红米K60"));
        //containsValue(Object value)  判断是否包含 value
        System.out.println(map.containsValue(249));

        //remove(Object key) 删除 key 对应的映射关系
        System.out.println(map.remove("小米手环9"));//删除小米手环9

        //Set<Map.Entry<K, V>> entrySet() 返回所有的 key-value 映射关系
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println("key:" + entry.getKey() + " value:" + entry.getValue());
        }
    }
}

3.4Set 的使用

3.4.1Set 的说明

Set Map 主要的不同有两点: Set 是继承自 Collection 的接口类, Set 中只存储了 Key。具体可以参考 [Set的官方文档]:

3.4.2Set的常用方法说明

方法解释
boolean add(E e)添加元素,但重复元素不会被添加成功
void clear()清空集合
boolean contains(Object o)判断 o 是否在集合中
Iterator 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中,可以达到去重的效果
注意:

 1. Set是继承自Collection的一个接口类

2. Set中只存储了key,并且要求key一定要唯一

3. TreeSet的底层是使用Map来实现的,其使用keyObject的一个默认对象作为键值对插入到Map中的

4. Set最大的功能就是对集合中的元素进行去重

5. 实现Set接口的常用类有TreeSetHashSet,还有一个LinkedHashSetLinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。

6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入

7. TreeSet中不能插入nullkeyHashSet可以。

8. TreeSet和HashSet的区别

Set底层结构
TreeSet
HashSet
底层结构
红黑树哈希桶
插入/删除/查找时间复杂度
O(log₂N)O(1)
是否有序
关于Key有序不一定有序
线程安全
不安全不安全
插入/删除/查找区别按照红黑树的特性来进行插入和删除1. 先计算key哈希地址 2. 然后进行 插入和删除
比较与覆写key必须能够比较,否则会抛出 ClassCastException异常自定义类型需要覆写equals和 hashCode方法
应用场景
需要Key有序场景下Key是否有序不关心,需要更高的 时间性能

3.4.3TreeSet的使用

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Set<String> set =new TreeSet<>();
        //add(E e)  添加元素,但重复元素不会被添加成功
        set.add("张三");
        set.add("李四");
        set.add("王五");
        System.out.println(set);
        System.out.println(set.add("张三"));

        //contains(Object o)  判断 o 是否在集合中
        System.out.println(set.contains("李四"));

        //size()  返回set中元素的个数
        System.out.println(set.size());

        //remove(Object o)  删除集合中的 o
        System.out.println(set.remove("王五"));

        //iterator()  返回迭代器
        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        }
        System.out.println();

        //addAll(Collection<? extends E> c)  将集合c中的元素添加到set中,可以达到去重的效果
        String[] str ={"李四","王五","赵六"};
        set.addAll(Arrays.asList(str));
        System.out.println(set);

        //containsAll(Collection<?> c)  集合c中的元素是否在set中全部存在,是返回true,否则返回 false
        System.out.println(set.containsAll(Arrays.asList(str)));
    }
}

4.总结

本次介绍了数据结构中关于Map和Set的概念、使用以及搜索树的几种操作,熟悉并掌握以上用法,为我们编写程序时提供更加方便的使用和操作。

  • 72
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值