数据结构-Java Map 和 Set-1

简介: 数据结构-Java Map 和 Set

前言

Set接口是继承与Collection的,而Map是独立的一个接口。

其中Set的实现类有:

  1. TreeSet
  2. HashSet


Map实现的类有:

  1. HashMap
  2. TreeMap


(HashSet和HashMap底层是一个哈希表,TreeSet和TreeMap底层是一棵搜索树(红黑树))


TreeMap和TreeSet,即java中利用搜索树实现的Map和Set,实际上应用的就是红黑树,而红黑树是一棵近似平衡的二叉搜索树(在二叉搜索树的基础上+上颜色以及红黑树性质的验证)


搜索树

定义树:

public class BinarySearchTree {
    static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root = null;
}


概念

二叉搜索树,也被称为二叉排序树(可能为一棵空树),具备以下性质:

  1. 若它的左子树不为空,则左子树上所有的节点的值,都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有的节点的值,都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树


二叉搜索树

中序遍历二叉搜索树,得到的结果是一组有序的数据。

查找


如上图,首先假设上图的二叉树已经存在,且根节点为root,要查找的值为val,如果root为空就返回null,定义一个cur节点来遍历这棵树,如果root.val > val,根据二叉搜索树的性质,就去root结点的左子树去找,否则就去右子树找,如果找到了(cur.val == val),就然后cur结点,直到cur == null,就返回空的结点。

/**
    *  查找二叉搜索树val的值
    *  val
    *
    */
    public TreeNode find(int val) {
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val == val) {
                return cur;
            } else if (cur.val < val) {
                cur = cur.right;
            } else {
                cur = cur.left;
            }
        }
        return null;
 
    }


插入

搜索树满足概念中性质a,b,c三点,如果要插入一个val。值首先定义一个cur节点,如果root == null就 new一个 值为val的节点newNode,并将root置为这个节点(root == newNode)

如果root != null,就进行搜索,如果root.val > val,就去左边继续搜索,如果root.val < val就去右边搜索。如果当前cur结点的左和右都为null,则直接比较插入即可

例如在上图中插入6,12 > 6,就去12的左树插入,12的左树的根值为5 < 6,所以去5这个节点的右树插入,其右树跟为9 > 6,又因为val为9这个结点的左和右都为null。所以直接在这个结点的左侧插入。

// 不使用parent结点
public void insert1(int val) {
        if (root == null) {
            root = new TreeNode(val);
            return;
        }
        TreeNode newNode = new TreeNode(val);
        TreeNode cur = root;
        while (cur.left != null && cur.right != null) {
            if (newNode.val > cur.val) {
                cur =cur.right;
            }else {
                cur =cur.left;
            }
        }
        if (newNode.val > cur.val) {
            cur.right = newNode;
        }else {
            cur.left = newNode;
        }
    }

当然也可以使用parent结点来记录cur的上一个结点,如果cur == null的时候,就可以找到cur的上一个结点parent,并在parent这个结点上插入。

public void insert2(int val) {
        if (root == null) {
            root = new TreeNode(val);
            return;
        }
        TreeNode newNode = new TreeNode(val);
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            parent = cur;
            if (cur.val < val) {
                cur =cur.right;
            }else {
                cur = cur.left;
            }
        }
        if (parent.val < val) {
            parent.right = newNode;
        }else {
            parent.left = newNode;
        }
    }

删除

二叉搜索树

如何删除

设cur节点为当前要删除的节点,parent为cur结点的父节点。

  1. 如果 cur.left == null

a.cur == root && root.left == null ,则root = cur.right。


b.cur != root && cur == parent.left ,则 parent.left = cur.right


c.cur != root && cur == parent.right,则 parent.right = cur.right

  1. 如果cur.right == null,则原理同上。
  1. cur == root,则root = root.left
  2. cur != root && cur == parent.left,则 parent.left = cur.left
  3. cur != root && cur == parent.right,则 prent.right = cur.left


图略

  1. cur.left != null && cur.right != null。

某个二叉搜索树

假设有这样一棵二叉搜索树。root为根节点,cur为当前将要被删除的节点。

该如何删除?

需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(值最小),用它的值填补到被

删除节点中,再删除这个最小值节点即可。

首先,你需要找出cur节点和cur的父节点:

实现

public void remove(int val) {
    TreeNode cur = root;
    TreeNode parent = null;
    while(cur != null){
        if (cur.val == val) {
            removeNode(parent, cur);
            break;
        }else if (cur.val > val) {
            parent = cur;
            cur = cur.left;
        }else {
             parent = cur;
            cur = cur.right;   
        }
    }
}


然后需要在cur的右树里面找到其最小值,这个最小值无非就是cur的右子树的第一个结点,或者是右子树上左树的最左的一个端点。找到这个节点之后,将其删除。

然后综上所述的cur.left == null 或者 cur.right == null的情况来完成removeNode这个方法;

private void removeNode(TreeNode parent, TreeNode cur) {
        // 要删除的节点的左子树为空
        if (cur.left == null) {
            if (cur == root) {
                root = cur.right;
            }else if (parent.left == cur) {
                parent.left = cur.right;
            }else {
                parent.right = cur.right;
            }
        // 要删除的节点的右子树为空
        }else if (cur.right == null) {
            if (cur == root) {
                root = cur.left;
            }else if (parent.left == cur) {
                parent.left = cur.left;
            }else {
                parent.right = cur.left;
            }
        }else/* suggests that:cur.left != null && cur.right != null */ {
            TreeNode target = cur.right;
            TreeNode targetParent = cur;
            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;
            }
        }
    }


性能分析

无论是插入,还是删除操作,都需要先查找,所以查找的速度代表了二叉搜索树中各个操作的性能。查找是根据节点的值比较来的,所以树越趋向于平衡(完全二叉树或者满二叉树),查找的次数就越趋向于树的深度,否则则会趋向于节点的个数,例如单分支的二叉搜索树:他的搜索时间复杂度为O(n)

完全二叉树

单分支二叉树(右单支)


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

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


数据结构-Java Map 和 Set-2

https://developer.aliyun.com/article/1517090

目录
相关文章
|
3天前
|
编译器 容器
哈希表模拟封装unordered_map和unordered_set
哈希表模拟封装unordered_map和unordered_set
|
3天前
|
编译器 测试技术 计算机视觉
红黑树模拟封装map和set
红黑树模拟封装map和set
|
1月前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
|
2月前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
60 5
|
2月前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
73 20
|
2月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
79 18
你对Collection中Set、List、Map理解?
|
3月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
37 5
|
3月前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
62 3
【C++】map、set基本用法
|
3月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
69 6
|
3月前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。

热门文章

最新文章