【182】Java8利用二叉查找树实现Map

news2024/11/16 18:39:13

本文利用二叉查找树写了一个Map,用来保存键值对。

二叉查找树的定义

二叉查找树又名二叉搜索树,英文名称是 Binary Search Tree,缩写BST。

二叉排序树,英文名称是 Binary Sorted Tree,缩写BST。

二叉查找树、二叉搜索树、二叉排序树,这三者是一回事,只是名字不同。

二叉查找树的定义如下:

二叉查找树或者是一棵空树,或者是具有下列性质的二叉树:

  1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值。

  2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。

  3. 它的左、右子树也分别为二叉查找树。

二叉查找树的时间复杂度和空间复杂度

在这里插入图片描述

Java8代码实现

二叉查找树的节点类:

package zhangchao.bst;

/**
 *
 * 二叉查找树的节点类。
 *
 * @author zhangchao
 *
 * @param <K> 二叉查找树的键。
 * @param <V> 二叉查找树的值。
 */
public class BstTreeMapNode<K,V> {

    // 键
    public K key = null;
    // 值
    public V value = null;

    // 父节点的引用。
    public BstTreeMapNode parent = null;
    // 左子节点的引用。
    public BstTreeMapNode left = null;
    // 右子节点的引用。
    public BstTreeMapNode right = null;


}

二叉查找树写的 BstTreeMap

package zhangchao.bst;

import java.util.*;

/**
 * 利用二叉查找树实现的Map
 *
 * @author zhangchao
 *
 * @param <K> 键
 * @param <V> 值
 */
public class BstTreeMap<K, V> {

    // 根节点
    private BstTreeMapNode root = null;

    // 用户自定义的比较器
    private Comparator<K> comparator;

    /**
     * 构造方法
     * @param comparator 用户自定义的比较器
     */
    public BstTreeMap(Comparator<K> comparator) {
        this.comparator = comparator;
    }

    /**
     * 向map中放入键值对
     * @param key  键
     * @param value 值
     */
    public void put(K key, V value) {
        if (null == root) {
            root = new BstTreeMapNode<K, V>();
            root.parent = null;
            root.key = key;
            root.value = value;
            return;
        }

        BstTreeMapNode<K, V> parent = null;
        BstTreeMapNode<K, V> current = root;
        int compareResult = 0;
        while (null != current) {
            compareResult = this.comparator.compare(key, current.key);
            if (compareResult < 0) {
                parent = current;
                current = current.left;
            } else if (compareResult > 0) {
                parent = current;
                current = current.right;
            } else {
                // 有相等的key,直接设置值就可以了。
                current.value = value;
                return;
            }
        }

        BstTreeMapNode<K, V> newItem = new BstTreeMapNode<K, V>();
        newItem.key = key;
        newItem.value = value;
        newItem.parent = parent;
        if (compareResult < 0) {
            parent.left = newItem;
        } else if (compareResult > 0) {
            parent.right = newItem;
        }
    }

    /**
     * 根据键获取值
     * @param key 键
     * @return 值
     */
    public V get(K key) {
        BstTreeMapNode<K, V> current = root;
        int compareResult;
        while (null != current) {
            compareResult = this.comparator.compare(key, current.key);
            if (compareResult < 0) {
                current = current.left;
            } else if (compareResult > 0) {
                current = current.right;
            } else {
                // 有相等的key,直接返回值就可以了。
                return current.value;
            }
        }
        return null;
    }

    /**
     * 获取键的列表,按照比较器的顺序排列。
     * @return 键的列表,按照比较器的顺序排列。
     */
    public List<K> keyList() {
        if (null == root) {
            return new ArrayList<K>();
        }
        List<K> result = new ArrayList<K>();

        Stack<BstTreeMapNode> stack = new Stack<>();
        BstTreeMapNode<K, V> current = root;
        do {
            while (null != current) {
                stack.push(current);
                current = current.left;
            }
            current = stack.pop();
            result.add(current.key);
            current = current.right;
        } while (!stack.isEmpty() || null != current);

        return result;
    }

    /**
     * 是否为空。
     * @return true表示为空;false表示不为空。
     */
    public boolean isEmpty() {
        return null == root;
    }

    /**
     * 根据键获取 BST 树的节点。
     * @param key 键
     * @return BST树的节点
     */
    public BstTreeMapNode<K, V> getNode(K key) {
        BstTreeMapNode<K, V> current = root;
        int compareResult;
        while (null != current) {
            compareResult = this.comparator.compare(key, current.key);
            if (compareResult < 0) {
                current = current.left;
            } else if (compareResult > 0) {
                current = current.right;
            } else {
                // 有相等的key,直接返回值就可以了。
                return current;
            }
        }
        return null;
    }

    /**
     * 根据键删除键值对。
     * @param key 键
     */
    public void remove(K key) {
        BstTreeMapNode<K, V> node = getNode(key);
        if (null == node) {
            return;
        }
        // 叶子节点
        if (null == node.left && null == node.right) {
            if (node == root) {
                this.root = null;
            } else if (node == node.parent.left) {
                node.parent.left = null;
            } else if (node == node.parent.right) {
                node.parent.right = null;
            }
            node.parent = null;
        }
        // 只有左子节点
        else if (null != node.left && null == node.right) {
            if (node == root) {
                this.root = node.left;
            } else if (node == node.parent.left) {
                node.parent.left = node.left;
            } else if (node == node.parent.right) {
                node.parent.right = node.left;
            }
            node.left.parent = node.parent;
            node.parent = null;
            node.left = null;
        }
        // 只有右子节点
        else if (null == node.left && null != node.right) {
            if (node == root) {
                this.root = node.right;
            } else if (node == node.parent.left) {
                node.parent.left = node.right;
            } else if (node == node.parent.right) {
                node.parent.right = node.right;
            }
            node.right.parent = node.parent;
            node.parent = null;
            node.right = null;
        }
        // 不是根节点并且有左右子节点。
        else {
            BstTreeMapNode<K,V> current = node.left;
            while (null != current.right) {
                current = current.right;
            }
            if (current == current.parent.left) {
                current.parent.left = current.left;
            } else if (current == current.parent.right) {
                current.parent.right = current.left;
            }
            if (null != current.left) {
                current.left.parent = current.parent;
            }
            current.parent = node.parent;
            current.left = node.left;
            current.right = node.right;

            if (null != node.left) {
                node.left.parent = current;
            }
            if (null != node.right) {
                node.right.parent = current;
            }

            if (null == node.parent) {
                this.root = current;
            } else if (node == node.parent.left) {
                node.parent.left = current;
            } else if (node == node.parent.right) {
                node.parent.right = current;
            }

            // 清理被删除节点的关联引用
            node.left = null;
            node.right = null;
            node.parent = null;
        }
    }

    /**
     * 计算map的键值对数量。
     * @return map的键值对数量。
     */
    public int size() {
        return this.keyList().size();
    }

    /**
     * 清空map,删除所有的键值对。
     */
    public void clear() {
        List<K> keyList = this.keyList();
        if (null != keyList && !keyList.isEmpty()) {
            for (K key : keyList) {
                this.remove(key);
            }
        }
    }

    private void showTree(BstTreeMapNode node, int level, String prefix) {
        if (null == node) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < level; i++) {
            sb.append("  ");
        }
        sb.append(prefix);
        sb.append(node.key).append("         ");
        if (node.parent != null) {
            sb.append(node.parent.key);
        }
        System.out.println(sb);
        level++;
        showTree(node.left, level, "left : ");
        showTree(node.right, level, "right: ");
    }

    /**
     * 打印树形结构。
     */
    public void showTree() {
        if (null == root) {
            System.out.println("null");
        }
        showTree(root, 0, "root: ");
    }


}

测试例子

TestBST

package zhangchao.bst;

import java.util.Comparator;
import java.util.List;

public class TestBST {
    public static void test1() {
        BstTreeMap<Integer, String> map = new BstTreeMap<>( (o1, o2) -> (o1 - o2) );
        int[] arr = new int[]{8,3,6,1,2,98,2,6};
        for (int i : arr) {
            map.put(i, "__" + String.valueOf(i));
        }

        System.out.println(map.get(3));
        System.out.println(map.get(6));
        System.out.println(map.get(98));

        List<Integer> keyList = map.keyList();
        for (Integer key : keyList) {
            String value = map.get(key);
            System.out.println(key + " : " + value);
        }
        System.out.println();
        map.showTree();

        map.remove(2);
        System.out.println(map.get(2));
    }


    public static void test2() {
        BstTreeMap<String, String> map = new BstTreeMap(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                if (null == o1 && null == o2) {
                    return 0;
                }
                if (null == o1 && null != o2) {
                    return -1;
                }
                if (null != o1 && null == o2) {
                    return 1;
                }
                return o1.compareTo(o2);
            }
        });

        String[] arr = {"j", "h", "e", "a", "e", "z", "d", "i", "y", "a", "u", "k", "u", "g", null};
        for (String i : arr) {
            map.put(i, "__" + String.valueOf(i));
        }
        System.out.println("Before:");
        map.showTree();
        String key = "z";
        map.remove(key);
        System.out.println(map.get(key));
        System.out.println("After:");
        map.showTree();

        System.out.println("map.get(null)=" + map.get(null));
        System.out.println("map.size()=" + map.size());
        map.remove(null);
        System.out.println("After remove(null), map.get(null)=" + map.get(null));
        System.out.println("After remove(null), map.showTree() ");
        map.showTree();
        System.out.println("map.isEmpty()=" + map.isEmpty());
        map.clear();
        System.out.println("After clear, map.showTree() ");
        map.showTree();
        System.out.println("map.isEmpty()=" + map.isEmpty());
    }

    // 测试删除根节点
    public static void test3() {
        BstTreeMap<Integer, String> map = new BstTreeMap<>( (o1, o2) -> (o1 - o2) );
        int[] arr = new int[]{100,50,150,25,75,125,190,12,40,60,30};
        for (int i : arr) {
            map.put(i, "__" + String.valueOf(i));
        }
        System.out.println("Before:");
        map.showTree();
        int key = 100;
        map.remove(key);
        System.out.println("map.get(100)" + map.get(key));
        System.out.println("After:");
        map.showTree();
        map.remove(75);
        System.out.println("map.get(75)" + map.get(75));
        System.out.println("After:");
        map.showTree();
    }

    public static void test4() {
        // 根节点是叶子节点
        BstTreeMap<Integer, String> map = new BstTreeMap<>( (o1, o2) -> (o1 - o2) );
        map.put(4, "_4");
        map.showTree();
        map.remove(4);
        System.out.println("map.get(4)=" + map.get(4));
        map.showTree();

        System.out.println("----------------------");

        // 根节点只有左子节点
        map.put(3, "_3");
        map.put(2, "_2");
        map.put(1, "_1");
        map.showTree();
        map.remove(3);
        System.out.println("map.get(3)=" + map.get(3));
        map.showTree();
        map.remove(2);
        System.out.println("map.get(2)=" + map.get(2));
        map.showTree();
        map.remove(1);
        System.out.println("----------------------");

        // 根节点只有右子节点
        map.put(1, "_1");
        map.put(2, "_2");
        map.put(3, "_3");
        map.showTree();
        map.remove(1);
        System.out.println("map.get(1)=" + map.get(1));
        map.showTree();
        map.remove(2);
        System.out.println("map.get(2)=" + map.get(2));
        map.showTree();
        map.remove(3);
        System.out.println("----------------------");
    }

    public static void test5() {
        BstTreeMap<Integer, String> map = new BstTreeMap<>( (o1, o2) -> (o1 - o2) );
        int[] arr = new int[]{100,50,150,25,75,125,190,12,40,60,30};
        for (int i : arr) {
            map.put(i, "__" + String.valueOf(i));
        }
        System.out.println("Before:");
        map.showTree();
//        int key = 50;
        int key = 150;
        map.remove(key);
        System.out.println(map.get(key));
        System.out.println("After:");
        map.showTree();
    }



    public static void main(String[] args) {
        test2();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

excel实用技巧:如何构建多级下拉菜单

使用数据有效性制作下拉菜单对大多数小伙伴来说都不陌生&#xff0c;但说到二级和三级下拉菜单大家可能就不是那么熟悉了。什么是二级和三级下拉菜单呢&#xff1f;举个例子&#xff0c;在一个单元格选择某个省后&#xff0c;第二个单元格选项只能出现该省份所属的市&#xff0…

vue-router原理简单实现

vue-router简单实现 初步预习 动态路由 获取id方式 第一种强依赖路由 第二种父传子方式&#xff08;推荐&#xff09; 嵌套路由 相同的头和尾&#xff0c;默认index&#xff0c;替换为detail 编程时导航 this.$router.push() this.$router.repleace() this.$router.g…

吊炸天,springboot的多环境配置一下搞明白了!

1、 使用springboot的profile命名规则profile用于多环境的激活和配置&#xff0c;用来切换生产&#xff0c;测试&#xff0c;本地等多套不通环境的配置。如果每次去更改配置就非常麻烦&#xff0c;profile就是用来切换多环境配置的。在Spring Boot框架中&#xff0c;使用Profil…

漏洞优先级排序的六大关键因素

当我们谈及开源漏洞时&#xff0c;我们会发现其数量永远处于增长状态。根据安全公司 Mend 研究发现&#xff0c;在 2022 年前九个月发现并添加到其漏洞数据库中的开源漏洞数量比 2021 年增加了 33%。该报告从 2022 年 1 月到 2022 年 9 月对大约 1,000 家北美公司进行了代表性抽…

一篇文章解决C语言操作符

我的主页&#xff1a;一只认真写代码的程序猿本文章是关于C语言操作符的讲解收录于专栏【C语言的学习】 目录 1、算术操作符 2、赋值操作符 3、关系操作符 4、条件操作符&#xff08;三目&#xff09; 5、逻辑操作符 6、单目操作符 7、移位操作符 8、位操作符 9、逗号…

使用Docker+Nignx部署vue项目

文章目录一、前言二、vue项目打包三、nginx基本介绍①nginx常用的功能&#xff1a;②nginx默认的主题配置文件解读③nginx目录解读三、docker内部署nginx①拉取nginx镜像②创建数据持久化目录☆☆☆③创建需要映射进去的文件④运行nginx四、大工告成最近&#xff08;之前&#…

2023年DAMA-CDGA/CDGP数据治理工程师认证(线上班)报名

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

gcc、g++,linux升级gcc、g++

安装cv-cuda库&#xff0c;要求gcc11&#xff0c;cmake>3.22版本。 Linux distro:Ubuntu x86_64 > 18.04WSL2 with Ubuntu > 20.04 (tested with 20.04) CUDA Driver > 11.7 (Not tested on 12.0) GCC > 11.0 Python > 3.7 cmake > 3.22gcc、g介绍 参考&…

手把手安装GNN必备库 —— pytorch_geometric

0 BackGround GNN&#xff1a;图神经网络&#xff0c;由于传统的CNN网络无法表示顶点和边这种关系型数据&#xff0c;便出现了图神经网络解决这种图数据的表示问题&#xff0c;这属于CNN往图方向的应用扩展。 GCN&#xff1a;图卷积神经网络&#xff0c;GNN在训练过程中&#…

【ONE·R || 两次作业(二):GEO数据处理下载分析】

总言 两次作业汇报其二&#xff1a;GEO数据处理学习汇报。    文章目录总言2、作业二&#xff1a;GEO数据处理下载分析2.1、GEO数据库下载前准备2.2、GEO数据库下载及数据初步处理2.2.1、分阶段解析演示2.2.1.1、编号下载流程2.2.1.2、对gset[ 1 ]初步分析2.2.1.3、对gset[ 2…

基于requests框架实现接口自动化测试项目实战

requests库是一个常用的用于http请求的模块&#xff0c;它使用python语言编写&#xff0c;在当下python系列的接口自动化中应用广泛&#xff0c;本文将带领大家深入学习这个库&#xff0c;Python环境的安装就不在这里赘述了&#xff0c;我们直接开干。 01 requests的安装 win…

销售结束语话术

销售要记住&#xff0c;结束语不代表结束&#xff0c;而是下一次沟通的开始&#xff0c;所以销售要学会通过结束语来为自己争取下次沟通的机会。 前言 不论是哪一行业&#xff0c;对于销售而言&#xff0c;大多数成交的客户都是经过持续有效的跟踪的&#xff0c;还会出现有很多…

Java设计模式-原型模式Prototype

介绍 当我们有一个类的实例&#xff08;Prototype&#xff09;并且我们想通过复制原型来创建新对象时&#xff0c;通常使用Prototype模式。 原型模式是一种创建型设计模式。能够复制已有对象&#xff0c; 而又无需使代码依赖它们所属的类。 场景举例 现在有一只羊 tom&#xf…

iTerm2连接ssh配置

iTerm2连接ssh配置 #首先在/Users目录下按照如下命令创建sh脚本 cd /Users/#创建iterm文件夹 mkdir iterm#进入iterm文件夹 cd iterm#创建myserver.sh文件 touch myserver.sh#编辑myserver.sh文件 vi myserver.sh如果出现没有权限&#xff0c;就命令前面加上sudo 键盘输入i编…

斯皮尔曼相关(spearman)相关性分析一文详解+python实例代码

前言 相关性分析算是很多算法以及建模的基础知识之一了&#xff0c;十分经典。关于许多特征关联关系以及相关趋势都可以利用相关性分析计算表达。其中常见的相关性系数就有三种&#xff1a;person相关系数&#xff0c;spearman相关系数&#xff0c;Kendalls tau-b等级相关系数…

Java + OpenCv 根据PID/VID调用指定摄像头

问题&#xff1a; 主机接入了多个USB摄像头&#xff0c;传统的OpenCv是用摄像头插入usb的下标调取的&#xff0c;如过只接入一个摄像头那直接使用capture.open(0);这种方式调用没有任何问题&#xff0c;多个的话&#xff0c;就会出现问题&#xff0c;因为USB拔插时候对应摄像头…

用原生的方式写vue组件之深度剖析组件内部的原理

目录前言一&#xff0c;对组件的复习及理解二&#xff0c;模块化与组件化三&#xff0c;用原生的方式写vue组件3.1 准备工作3.2 创建组件3.3 组件中的data为什么是函数式写法3.4 组件中的template四&#xff0c;注册组件五&#xff0c;使用组件六&#xff0c;全局组件七&#x…

阿里云服务器ECS购买教程

本文是关于阿里云主机&#xff08;服务器ECS&#xff09;购买流程的一个详细介绍。阿里云服务器&#xff08;Elastic Compute Service&#xff0c;简称 ECS&#xff09;是一种简单高效、处理能力可弹性伸缩的计算服务&#xff0c;帮助您快速构建更稳定、安全的应用&#xff0c;…

机器学习实战教程(十二):线性回归提高篇

一、前言本篇文章讲解线性回归的缩减方法&#xff0c;岭回归以及逐步线性回归&#xff0c;同时熟悉sklearn的岭回归使用方法&#xff0c;对乐高玩具套件的二手价格做出预测。二、岭回归如果数据的特征比样本点还多应该怎么办&#xff1f;很显然&#xff0c;此时我们不能再使用上…

【Elsevier出版社】1区智能物联网类SCIEI,审稿友好~

1区智能物联网类SCI&EI 【出版社】Elsevier 【期刊简介】IF&#xff1a;5.5-6.0&#xff0c;JCR1区&#xff0c;中科院3区 【检索情况】SCI&EI 双检&#xff0c;正刊 【参考周期】3个月左右录用 【截稿日期】2023.2.28 【征稿领域】 ①物联网辅助的智能解决方案…