【数据结构】Set和Map(常用方法、手动实现)

目录

一、基本概念

二、二叉搜索树

三、手动实现

总结


一、基本概念

        Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。Set我们可以理解为集合,Map我们可以理解为字典。其中Set继承自collection接口,Map独立存在。Set中的基本元素为Key。Map中的基本元素为Key-value对。无论在Set还是在Map中,Key的值都不允许重复

        Map和Set可以通过什么来实现呢?我们也可以通过List来实现,但是我们发现效率太低,于是我们引出两种经典的数据结构以实现Set和Map。分别是二叉搜索树(Binary Search Tree)和哈希表(Hash Table)。其中二叉搜索树我们比较熟悉。

Set接口:

方法解释
void clear()清空集合
boolean add(E e)添加元素,但重复元素不会被添加成功
boolean contains(Object o)判断 o 是否在集合中
boolean remove(Object o)删除集合中的 o
int size()返回set中元素的个数
boolean isEmpty()检测set是否为空,空返回true,否则返回false
Iterator iterator() 返回迭代器
Object[] toArray()将set中的元素转换为数组返回
boolean containsAll(Collection c) 集合c中的元素是否在set中全部存在,是返回true,否则返回 false
boolean addAll(Collection c) 将集合c中的元素添加到set中,可以达到去重的效果

对于Set的两种实现方式比较

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

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

对于Map的两种实现方式的比较

Map底层结构TreeMapHashMap
底层结构红黑树哈希桶
插入/删除/查找时间 复杂度O(log n)O(1)
是否有序关于Key有序无序
线程安全不安全不安全
插入/删除/查找区别需要进行元素比较通过哈希函数计算哈希地址
比较与覆写key必须能够比较,否则会抛出 ClassCastException异常自定义类型需要覆写equals和 hashCode方法
应用场景需要Key有序场景下Key是否有序不关心,需要更高的 时间性能

二、二叉搜索树

二叉搜索树满足如下性质:

        1、若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

        2、 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

        3、它的左右子树也分别为二叉搜索树

三、手动实现

        我们自定义一个TreeNode类方便我们手动实现,我们只是对一些简单的基础功能进行实现,实现的结果如下:

package set_map.set.treeSet;

public class TreeNode {
    int key;
    TreeNode left;
    TreeNode right;

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

    @Override
    public String toString() {
        return "TreeNode{" +
                "key=" + key +
                ", left=" + left +
                ", right=" + right +
                '}';
    }
}
package set_map.set.treeSet;

public class BSTree {
    private TreeNode root=null;

    public boolean contains(int dest){
        TreeNode cur=root;
        while (cur!=null){
            if(cur.key==dest){
                return true;
            }else if(cur.key<dest){
                cur=cur.right;
            }else{
                cur=cur.left;
            }
        }
        return false;
    }


    //给定顺序不同,得到的搜索树的结构也不同
    public boolean add(int dest){
        TreeNode treeNode = new TreeNode(dest);
        TreeNode parent=null;//定义一个节点用于记录双亲结点

        if(root==null){
            root=treeNode;
            return true;
        }

        TreeNode cur=root;
        while(cur!=null){
            if(cur.key==dest){
                return false;
            }else if(dest<cur.key){
                parent=cur;
                cur=cur.left;
            }else {
                parent=cur;
                cur=cur.right;
            }
        }
        //此时到了null的位置,是插入的时候了
        if(dest<parent.key){
            parent.left=treeNode;
        }else {
            parent.right=treeNode;
        }
        return true;
    }
    public boolean remove(int key){
        TreeNode parent=null;
        TreeNode cur=root;
        while(cur!=null){
            if(cur.key==key){
                deleteKey(parent,cur,key);
                return true;
            }else if(key< cur.key){
                parent=cur;
                cur=cur.left;
            }else{
                parent=cur;
                cur=cur.right;
            }
        }
        return false;
    }

    private void deleteKey(TreeNode parent,TreeNode node, int key) {
        if(node.left==null){
            if(node==root){
                root=root.right;
            }else if(node==parent.left){
                parent.left=node.right;
            }else{
                parent.right=node.right;
            }
        }else if(node.right==null){
            if(node==root){
                root=root.left;
            }else if(node==parent.left){
                parent.left=node.left;
            }else {
                parent.left=node.left;
            }
        }else{
            replaceDelete(parent,node);
        }
    }

    private void replaceDelete(TreeNode parent, TreeNode node) {
        TreeNode canparent=node;
        TreeNode candidate =node.left;
        while (candidate.right!=null){
            canparent=candidate;
            candidate=candidate.right;
        }
        //将值进行替换
        node.key= candidate.key;
        //删除候选人的节点
        if(canparent==node){
            canparent.left=candidate.left;
        }else {
            canparent.right=null;
        }
    }

    public static void main(String[] args) {
        int[] keys={9,7,13,21,5,8,16,32,4,0,1,6,2,3};
        BSTree bsTree = new BSTree();
        for (int key : keys) {
            bsTree.add(key);
        }
        System.out.println(true);
        int[] re={2,6,7,13,9};
        for (int i : re) {
            bsTree.remove(i);
        }
        System.out.println(true);
    }

}

总结

        这些方法一定要牢记于心。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值