【数据结构】二叉搜索树与Map和Set

这篇具有很好参考价值的文章主要介绍了【数据结构】二叉搜索树与Map和Set。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

♫二叉搜索树

♪什么是二叉搜索树

♪二叉搜索树的特性

♪模拟实现二叉搜索树

♫Map

♪什么是Map

♪Map的内部类

♪Map的常用方法

♪Map的遍历

♫Set 

♪什么是Set

♪Set的常用方法

♪Set的遍历


♫二叉搜索树

♪什么是二叉搜索树

二叉搜索树又称二叉排序树,是一种特殊的二叉树,这颗树的 左子树上所有节点的值小于根节点的值, 右子树上所有节点的值大于根节点的值,且其 左右子树都必须也是二叉搜索树。

♪二叉搜索树的特性

①一棵二叉搜索树的中序遍历结果是一个递增的有序序列。②.根据值的大小关系进行查找、插入、删除等操作非常高效。③.由于二叉搜索树左子树的节点值小于根节点,右子树的节点值大于根节点,故二叉搜索树还支持范围查询

♪模拟实现二叉搜索树

要想实现一颗二叉搜索树,首先需要确定二叉搜索树的结构。

♩定义节点

以静态内部类的方式定义二叉树的节点类型,每个结点应该包含数据元素、左子树指针和右子树指针三个成员变量:

public class BinarySearchTree {
    static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;
        //初始化节点值的构造方法
        public TreeNode(int val) {
            this.val = val;
        }
    }
}
♩成员属性
用于定位和操作该二叉搜索树:
    private TreeNode root = null;
确定完这些,接下来就可以来实现二叉树的基本操作了。
♩插入操作
插入一个节点可以通过比较插入元素和当前节点的大小关系,如果插入元素小于当前节点的值,就往左子树递归插入,否则往右子树递归插入:
    //插入操作
    public boolean insert(int val) {
        //根节点为空:
        if (root == null) {
            root = new TreeNode(val);
            return true;
        }
        //根节点不为空:
        TreeNode curParent = root;//cur的父节点
        TreeNode cur = root;
        //找到合适的插入位置
        while (cur != null) {
            curParent = cur;
            //值小,位置在左子树方向
            if (val < cur.val) {
                cur = cur.left;
            }
            //值大,位置在右子树方向
            if (val > cur.val) {
                cur = cur.right;
            }
            //二叉搜索树中已经有该值了,不能再插入相同值
            if (val == cur.val) {
                return false;
            }
        }
        //实例化一个新节点
        TreeNode node = new TreeNode(val);
        //值小,在父节点的左子树
        if (val < curParent.val) {
            curParent.left = node;
        }
        //值大,在父节点的右子树
        if (val > cur.val) {
            curParent.right = node;
        }
        //插入成功
        return true;
    }

♩查找操作

查找一个节点需要从根节点开始查找,如果当前节点值等于待查找元素的值,则返回当前节点;否则,根据比较结果查找其左子树或右子树直到找到为止:

    //查找查找
    public TreeNode search(int val) {
        //不能直接操作根节点,不然下次就找不到根节点了
        TreeNode cur = root;
        while (cur != null) {
            //找到了,返回该节点
            if (val == cur.val) {
                return cur;
            }
            //值比当前节点小,去左子数上找
            if (val < cur.val) {
                cur = cur.left;
            }
            //值比当前节点大,去右子树上找
            if (val > cur.val) {
                cur = cur.right;
            }
        }
        //都没找到,返回null
        return null;
    }
♩删除操作
删除一个节点需要考虑其左子树和右子树的情况。如果该节点左子树为空,直接将其替换为右子树;如果右子树为空,直接将其替换为左子树;否则,将其右子树的最小节点替换该节点,并删除该最小节点:
    //删除操作
    public void remove(int val) {
        TreeNode curParent = root;
        TreeNode cur = root;
        //找到要删除的节点
        while (cur != null) {
            curParent = cur;
            //值小,要删除的节点在左子树方向
            if (val < cur.val) {
                cur = cur.left;
            }
            //值大,要删除的节点在右子树方向
            if (val > cur.val) {
                cur = cur.right;
            }
            //值相同,找到要删除的节点
            if (val == cur.val) {
                //删除该节点
                removeNode(curParent, cur);
            }
        }
    }
    //刷除cur节点
    private void removeNode(TreeNode curParent, TreeNode cur) {
        //如果cur的左子树为空
        if (cur.left == null) {
            if (cur == root) {
                //cur为根节点
                root = cur.right;
            } else if (cur == curParent.left) {
                //cur为左孩子节点
                curParent.left = cur.right;
            } else if (cur == curParent.right) {
                //cur为右孩子节点
                curParent.right = cur.right;
            }
            return;
        }
        //如果cur的右子树为空
        if (cur.right == null) {
            if (cur == root) {
                //cur为根节点
                root = cur.left;
            } else if (cur == curParent.left) {
                //cur为左孩子节点
                curParent.left = cur.left;
            } else if (cur == curParent.right) {
                //cur为右孩子节点
                curParent.right = cur.left;
            }
            return;
        }
        //如果cur的左右子树都不为空
        if (cur.left != null && cur.right != null) {
            TreeNode tarParent = cur;
            TreeNode tar = cur.right;
            //找到cur的右子树中最左边的那个节点(该节点比cur的左子树大,比cur的右子树的其它节点大)
            while (tar.left != null) {
                tarParent = tar;
                tar = tar.left;
            }
            cur.val = tar.val;
            if (tar == tarParent.left) {
                //当cur的右孩子节点存在左孩子节点的时候
                tarParent.left = tar.right;
            } else if (tar == tarParent.right) {
                //当cur的右孩子节点不存在左孩子节点的时候
                tarParent.right = tar.right;
            }
        }
    }

♩性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。对有n 个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树的高度越高,查找效率越低。
对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
【数据结构】二叉搜索树与Map和Set,数据结构,数据结构,算法,java,开发语言
最优情况下(二叉搜索树为完全二叉树),其时间复杂度为:O(logn)
最差情况下(二叉搜索树退化为单支树),其时间复杂度为:O(n)
因此,为了保证每个节点的左右子树高度相差不超过1,这才有了AVL树(通过左旋,右旋,左右双旋调整高度差),而为了减少AVL的旋转次数,这才有了红黑树(通过染色来保证平衡,不需要每次都调整,复杂度相对较低)。

♫Map

什么是Map

Map是Java集合中一种专门用来进行搜索的容器或者数据结构,它适合多态查找(查找时能进行一些插入和删除的操作),其搜索的效率与其具体的实例化子类有关。
【数据结构】二叉搜索树与Map和Set,数据结构,数据结构,算法,java,开发语言
Map 是一个接口类,该类没有继承自 Collection ,该类中存储的是 <K,V> 的键值对,并且K一定是唯一的,不 能重复

Map的内部类

在Map内部有一个用来存放Map<key,value>键值对映射关系的内部类:Map.Entry<K,V>,

该内部类中主要提供了<key, value>的获取, value 的设置以及 Key 的比较方式:
方法 描述
K getKey()
返回 entry 中的 key
V getValue()
返回 entry 中的 value
V setValue(V value)
将键值对中的 value 替换为指定 value

注:Map.Entry<K,V>并没有提供设置Key的方法,即Map中Key是不能被改变的。

Map的常用方法

方法 描述
V put(K key, V value)
设置 key 对应的 value
V get(Object key)
返回 key 对应的 value
V getOrDefault( Object key, V defaultValue)
返回 key 对应的 value key 不存在,返回默认值
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的遍历

因为Map是一个独立的接口,没有继承Iterable接口,故不可直接通过for-each和迭代器进行遍历。我们可以借助上述常用方法返回的集合(继承了Iterable接口)进行遍历操作。

①.使用for-each循环遍历Map中的键值对:

public class Test {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        for(Map.Entry<String, Integer> entry : map.entrySet()){
            String key = entry.getKey();
            Integer value = entry.getValue();
            // 对key和value进行操作
        }
    }
}

②.使用Iterator遍历Map中的键值对:

public class Test {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            String key = entry.getKey();
            Integer value = entry.getValue();
            // 对key和value进行操作
        }
    }
}

③.遍历Map中的键

public class Test {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        for(String key : map.keySet()){
            // 对key进行操作
        }
    }
}

④.遍历Map中的值

public class Test {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        for(Integer value : map.values()){
            // 对value进行操作
        }
    }
}

Map的实现类

实现类 TreeMap HashMap
底层结构 红黑树 哈希桶
插入/删除/查找的时间复杂度 O(logn) O(1)
是否有序 关于key有序 无序
线程安全 不安全 不安全
插入/删除/查找的区别 不能插入空值,按照红黑树的特性来进行插入和删除 能插入空值,通过哈希函数计算哈希地址
比较与覆写
key 必须能够比较(有传比较器优先根据比较器比较,否则根据compareTo方法比较)
自定义类型需要覆写 equals 和hashCode方法
应用场景
需要 Key 有序场景下
Key 是否有序不关心,需要更高的时间性能

♫Set 

什么是Set

Set和Map一样也是Java集合中一种专门用来进行搜索的容器或者数据结构,它一样适合多态查找(查找时能进行一些插入和删除的操作),Set与Map不同的是:①.Set是继承自Collection的接口类。②.Set中只存储了Key。【数据结构】二叉搜索树与Map和Set,数据结构,数据结构,算法,java,开发语言

注:

1.Set是继承自Collection的一个接口类
2. Set中只存储了key,并且要求key一定要唯一
3. Set的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
4. Set最大的功能就是对集合中的元素进行去重
5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。
6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入

Set的常用方法

方法 描述
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的遍历

Set继承了Iterable接口,故可以直接通过for-each和迭代器进行遍历

①.使用迭代器遍历Set:

public class Test {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        // 添加元素
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            // 处理元素
        }
    }
}

②.使用foreach循环遍历Set:

public class Test {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        // 添加元素
        for (String element : set) {
            // 处理元素
        }
    }
}

③.使用stream遍历Set:文章来源地址https://www.toymoban.com/news/detail-730961.html

public class Test {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        // 添加元素
        set.stream().forEach(element -> {
            // 处理元素
        });
    }
}

Set的实现类

实现类 TreeSet TreeMap
底层结构 红黑树 哈希桶
插入 / 删除 / 查找的时间复杂度
O(logn) O(1)
是否有序
关于key有序
不一定有序
线程安全
不安全 不安全
插入 / 删除 / 查找区别
不能插入空值, 按照红黑树的特性来进行插入和删除
能插入空值,通过哈希函数计算哈希地址
比较与覆写
key必须能够比较(有传比较器优先根据比较器比较,否则根据compareTo方法比较)
自定义类型需要覆写 equals
hashCode 方法
应用场景
需要 Key 有序场景下
Key 是否有序不关心,需要更高的
时间性能

到了这里,关于【数据结构】二叉搜索树与Map和Set的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构与算法——树与二叉树篇详解

    树形结构是一种非常重要的 非线性结构 ,树形结构中数据元素之间具有 一对多 的逻辑关系。 1.1.1 树的定义 树是由n(n=0)个结点所构成的有限集合 当n=0时,称为空树 当n0时,n个结点满足以下条件 有且仅有一个称为根的结点 其余结点可分为m个互不相交的有限集合,且每一个

    2024年02月06日
    浏览(50)
  • 【数据结构】树与二叉树(十三):递归复制二叉树(算法CopyTree)

      二叉树是一种常见的树状数据结构,它由结点的有限集合组成。一个二叉树要么是 空集 ,被称为 空二叉树 ,要么由一个根结点和两棵不相交的子树组成,分别称为 左子树 和 右子树 。每个结点最多有两个子结点,分别称为左子结点和右子结点。 引理5.1:二叉树中层数

    2024年02月01日
    浏览(40)
  • 数据结构与算法--二叉树与树(单选题含题解)

    1、对以下算法功能最准确的描述是()。 A.判断二叉树根结点值是否为e B.判断二叉树是否存在值为e结点 C.求二叉树中值为e结点的层次 D.求二叉树值为e的结点的个数 C 2、二叉树的形态 由 3 个结点可以构造出 ▁▁▁▁▁ 种不同形态的二叉树。 A.2 B.3 C.4 D.5 D 由n个结点可以构

    2024年02月03日
    浏览(41)
  • 【数据结构】搜索树 与 Java集合框架中的Set,Map

    作者主页:paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将javaSE基础知识一网打尽,希望可以帮到读者们哦。 其他专栏:《

    2024年02月08日
    浏览(38)
  • 【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)

    前面我们对 map / multimap / set / multiset 进行了简单的介绍,可以发现,这几个容器有个共同点是: 其底层都是按照 二叉搜索树 来实现的。 但是二叉搜索树有其自身的缺陷,假如 往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成 O(N) ,

    2024年02月06日
    浏览(44)
  • 数据结构--树与二叉树

    树的定义 树是n(n =0)个节点的有限集。当n=0 时,称为空树 在任意一棵非空树中应满足 有且仅有一个特定的称为根的结点 当n1 时,其余节点可分为m(m0) 个互不相交的有限集T1,T2……Tm,其中每个集合本身又是一棵树,并且称为根的子树 树是一种逻辑结构,也是一种分层结构 树的

    2024年02月22日
    浏览(45)
  • [数据结构] 树与二叉树

    树是由 (n(n geq 0)) 个节点组成的有限集。当 (n = 0) 时,称为空树。 任意一棵非空树应满足以下两点: (1)有且仅有一个特定的称为根的节点; (2)当 (n 1) 时,其余节点可分为 (m(m0)) 个互不相交的有限集 (T_1, T_2, dots, T_m) ,其中每个集合本身又是一棵树,称为根的

    2024年03月09日
    浏览(42)
  • 【数据结构】树与二叉树

    树是一种 非线性的数据结构 ,它是由n(n=0)个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的 。它具有以下的特点: 有一个特殊的结点,称为根结点,根结点没有前驱结点 除根结点外,其余结点被分

    2024年02月11日
    浏览(45)
  • 【数据结构】树与二叉树(中)

    目录 前言: 一、顺序存储结构: 二、堆: 1.堆的性质: 2.堆的性质: 3.堆的实现: Ⅰ.堆的初始化:  Ⅱ.堆的插入(含向上调整):  Ⅲ.堆的删除(含向下调整算法): Ⅳ.取堆顶的数据: Ⅴ.堆中的数据个数: Ⅵ.堆的判空:  Ⅶ.堆的销毁: 总结:         上篇文章中,

    2024年02月16日
    浏览(47)
  • 数据结构_树与二叉树

    目录 1. 树的基本概念 1.1 树的定义 1.2 基本术语 1.3 树的性质 1.4 相关练习 2. 二叉树的概念 2.1 二叉树的概念及其主要特性 2.2 二叉树的存储结构 2.2.1 顺序存储结构 2.2.2 链式存储结构 2.3 相关练习 3. 二叉树的遍历和线索二叉树 3.1 二叉树的遍历 3.1.1 先序遍历 3.1.2 中序遍历 3.1

    2024年02月04日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包