📒数据结构专栏简介: 内容主要以介绍数据结构为主, 并以一些题目辅以应用说明, 希望同学们看了之后能有所收获🉐
👷🏻♂️作者也是一名刚上路的小白coder🛵, 欢迎各位评论, 批评指正或学习交流👁🗨~~
文章目录
Map 和 Set
在介绍TreeMap
和 TreeSet
前, 首先要简单介绍一下Map
和Set
这两个接口, 因为TreeMap
和 TreeSet
分别实现了这两个接口, 知其然知其所以然嘛, 让我们了解下Map
和Set
究竟是"何方神圣", 上图 :
可以看到, Map
和Set
是在不同包名下的:
Map
是 java.util
包下的一个接口, 它的实现类主要是存储<K, V>
键值对, 且其中的K值是唯一的;
Set
是 java.lang
包下的一个接口, 是 Collection
的一个子接口, 存储的是K值, K值也不能重复
Map简介
Map
存储<K, V>
键值对, 且其中的K值是唯一的
<K, V>实际上是 Key - Value 模型, Key 和 Value 之间可以存在映射关系, 如: A班有50个学生, 键值对则可以写成 <A, 50>
而相对 Key - Value 模型的则是纯 Key 模型, 该模型只存储Key, 如: A班中的每个学生id或者名字
Map的一些注意事项
Map
是一个接口, 不能直接实例化, 若要使用需要实例化实现Map
的子类, 如TreeMap
,HashMap
Map
存储<K, V>
键值对, 且其中的Key
值是唯一的, 不能重复, 但Value
是可以重复的Map
中的<K, V>
键值对中的Key
不能直接修改,Value
可以进行修改,Key
只能先删除再重新插入Map
中的Key
可以分离出来存储在Set
中Map
中的Value
可以分离出来存储在Collection
中的子集合
Map的一些方法
方法 | 简述 |
---|---|
void clear() | 从此映射中移除所有映射关系 |
boolean containsKey(Object o) | 如果此映射包含指定键的映射关系,则返回 true |
boolean containsValue(Object o) | 如果此映射将一个或多个键映射到指定值,则返回 true |
Set<Map.Entry<K,V>>entrySet() | 返回此映射中包含的映射关系的 Set 视图 |
boolean equals(Object o) | 比较指定的对象与此映射是否相等 |
V get(Object key) | 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null |
boolean isEmpty() | 如果此映射未包含键-值映射关系,则返回 true |
Set keySet() | 返回此映射中包含的键的 Set 视图 |
V put(K key, V value) | 将指定的值与此映射中的指定键关联 |
V remove(Object key) | 如果存在一个键的映射关系,则将其从此映射中移除 |
int size() | 返回此映射中的键-值映射关系数 |
Collection values() | 返回此映射中包含的值的 Collection |
Entry<K,V>是一个Map内部的
interface
, Entry<K,V>提供了<Key, Value>
获取方法,Key
的获取,Value
的获取和修改,Map.entrySet()
用于遍历/** * A map entry (key-value pair). The <tt>Map.entrySet</tt> method returns * a collection-view of the map, whose elements are of this class. The * <i>only</i> way to obtain a reference to a map entry is from the * iterator of this collection-view. */
Set简介
Set
存储Key
值, 且其中的K值是唯一的, 存储的是纯Key
模型, 是一个不包含重复元素的 collection
Set的注意事项
-
Set
中不能插入null
-
Set
是Collection
下的一个子接口, 其实现类也是一个collection
, 因此可以在实例化其他实现Collection
接口的类时可以通过Set
构造对象, 如:Set<String> set = new HashSet<>(); set.add("a"); set.add("b"); set.add("c"); List<String> list = new ArrayList<>(set);//通过set实例化一个list
-
Set
中的Key
不能重复, 且存储的Key
不能直接修改, 只能先删除再插入新的Key
-
Set
的底层实际上是Map
, 插入时存储的是一个Key
和一个默认的Object
对象 -
Set
的最大的功能是创建了一个元素不会重复的collection
Set的一些方法
方法 | 简述 |
---|---|
boolean add(E e) | 如果set 中尚未存在指定的元素,则添加此元素 |
void clear() | 移除此set 中的所有元素 |
boolean contains(Object o) | 如果``set包含指定的元素,则返回 true` |
boolean equals(Object o) | 比较指定对象与此set 的相等性 |
boolean isEmpty() | 如果set 不包含元素,则返回 true |
boolean remove(Object o) | 如果set 中存在指定的元素,则将其移除 |
int size() | 返回set 中的元素数(其容量) |
Object[] toArray() | 返回一个包含set 中所有元素的Object数组 |
T[] toArray(T[] a) | 返回一个包含此set 中所有元素的数组, 返回数组的运行时类型是指定数组的类型 |
到这里可能会有一个疑问: 既然 Map 和 Set 中的K值都不能重复, Map 还能存储具有映射关系(K与V对应)的键值对, 为什么还要设置一个Set呢?
Map是不同于Collection的一个独立接口, 有时候我们需要用Map里的数据创建另外的结构, 就如我需要使用Map里的数据构建一个Heap(堆)
, 那么就可以先将Map
中的Key
存储到Set
中, 再通过Set
建堆
有了前面的铺垫, 我们终于可以看看今天的"主角" — TreeMap
与TreeSet
TreeMap
TreeMap的存储
源码注释说明: TreeMap的底层结构是一棵红黑树, 元素在TreeMap中是关于Key
有序存储的, 且元素间是可以比较的
/**
* A Red-Black tree based {@link NavigableMap} implementation.
* The map is sorted according to the {@linkplain Comparable natural
* ordering} of its keys, or by a {@link Comparator} provided at map
* creation time, depending on which constructor is used.
*/
红黑树
是一棵复杂的二叉搜索树
(或称二叉查找树), 什么是二叉搜索树呢?
我们规定结点中存储着可比较的元素, 树中的元素不会重复, 将任一结点视作根结点, 存储元素比根结点中元素小的结点为根结点的左子树, 则反之为右子树, 如下图构建一棵树:
这样就能获得一棵二叉搜索树, 红黑树的实现比较杂, 对红黑树感兴趣的小伙伴可以自行搜索, 这里仅模仿二叉搜索树的实现
二叉搜索树的模仿实现
基本代码如下:
public class BinarySearchTree {
static class TreeNode {
public int key;
public TreeNode left;
public TreeNode right;
TreeNode(int key) {
this.key = key;
}
}
public TreeNode root;
//插入
public boolean insert(int key);
//查找
public TreeNode search(int key);
//删除
public boolean remove(int key);
}
建议初学者动手画出代码运行流程的二叉树图, 能更快的上手和理解
查找
查找部分代码应该是最容易的, 只需利用二叉搜索树左子树key比父结点中的小, 右子树的key比父结点中的大的性质就能完成
//查找key是否存在
public TreeNode search(int key) {
TreeNode cur = root;
while (cur != null) {
if (key == cur.key) return cur;
if (key < cur.key) {
cur = cur.left;
} else {
cur = cur.right;
}
}
return null;
}
插入
从上述的二叉搜索树创建图中我们能发现: 每次将key插入树中时, 插入的位置一定是为null位置, 成为叶结点
//插入key
public boolean insert(int key) {
if (root == null) {
root = new TreeNode(key);
return true;
}
TreeNode cur = root;
TreeNode prev = root;
while (cur != null) {
if (key == cur.key) return false;
prev = cur;
if (key < cur.key) {
cur = cur.left;
} else {
cur = cur.right;
}
}
if (key < prev.key) {
prev.left = new TreeNode(key);
} else {
prev.right = new TreeNode(key);
}
return true;
}
删除
删除部分是比较麻烦的一部分代码, 因为要在删除后保证树还是一棵二叉搜索树
//删除key
public boolean remove(int key) {
//跟查找部分差不多, 先找到key与其父结点
TreeNode cur = root;
TreeNode prev = null;
while (cur != null) {
if (key == cur.key) break;
prev = cur;
if (key < cur.key) {
cur = cur.left;
} else {
cur = cur.right;
}
}
if (cur == null) {
return false;
}
return remove(prev, cur);
}
//删除时要注意的是 root, cur 和 cur的子树的情况
private boolean remove(TreeNode parent, TreeNode cur) {
//cur左子树为空时
if (cur.left == null) {
//若cur为root 则可以直接用右子树将替换cur
if (cur == root) {
root = cur.right;
//若不为root, 则可能为父结点的左子树或右子树
} else if (cur == parent.left) {
parent.left = cur.right;
} else {
parent.right = cur.right;
}
//cur右子树为空时, 情况与上述相似
} else if (cur.right == null) {
if (cur == root) {
root = cur.left;
} else if (cur == parent.left) {
parent.left = cur.left;
} else {
parent.right = cur.left;
}
} else {
//左右子树都不为空时, 则要使用"替罪羊"方法
TreeNode targetParent = cur;
TreeNode target = cur.right;
//选取右子树为target, 从target的左子树开始循环, 直至target的左子树为空, 此时target便是要找的"替罪羊"
while (target.left != null) {
targetParent = cur;
target = target.left;
}
//交换cur和traget, 为其"替罪"
cur.key = target.key;
//当要也要考虑target的左子树一开始就为空的情况
if (target != targetParent.right) {
targetParent.left = target.right;
} else {
targetParent.right = target.right;
}
}
return true;
}
TreeMap的创建和常用方法
创建
//使用键的自然顺序构造一个新的、空的树映射
TreeMap<K, V> treeMap = new TreeMap<K,V>();
//构造一个新的、空的树映射,该映射根据给定比较器进行排序
// 对应构造方法:
TreeMap(Comparator<? super K> comparator) ;
//创建
TreeMap<K, V> treeMap = new TreeMap<K,V>(new Comparator<K>() {
@Override
public int compare(K o1, K o2) {
return 0;
}
});
//构造一个与给定映射具有相同映射关系的新的树映射,该映射根据其键的自然顺序进行排序
// 对应构造方法:
TreeMap(Map<? extends K,? extends V> m) ;
//如利用已有Hashtable创建:
Hashtable<K, V> table;//假设table完成初始化
TreeMap<K, V> treeMap = new TreeMap<K,V>(table);
//构造一个与指定有序映射具有相同映射关系和相同排序顺序的新的树映射
// 对应构造方法:
TreeMap(SortedMap<K,? extends V> m) ;
//如利用已有HashMap创建:
HashMap<K, V> map;//假设map完成初始化
TreeMap<K, V> treeMap = new TreeMap<K,V>(map);
常用方法
方法 | 简述 |
---|---|
boolean containsKey(Object key) | 如果此映射包含指定键的映射关系,则返回 true |
boolean containsValue(Object value) | 如果此映射为指定值映射一个或多个键,则返回 true |
Set<Map.Entry<K,V>> entrySet() | 返回此映射中包含的映射关系的 Set 视图 |
V get(Object key) | 返回指定键所映射的值,如果对于该键而言,此映射不包含任何映射关系,则返回 null |
Set keySet() | 返回此映射包含的键的 Set 视图 |
V put(K key, V value) | 将指定值与此映射中的指定键进行关联 |
V remove(Object key) | 如果此 TreeMap 中存在该键的映射关系,则将其删除 |
int size() | 返回此映射中的键-值映射关系数 |
SortedMap<K,V> subMap(K fromKey, K toKey) | 返回此映射的部分视图,其键值的范围从 fromKey (包括)到 toKey (不包括) |
SortedMap<K,V> tailMap(K fromKey) | 返回此映射的部分视图,其键大于等于 fromKey |
TreeSet
TreeSet的存储
TreeSet
的底层是基于TreeMap
实现的
/**
* A {@link NavigableSet} implementation based on a {@link TreeMap}.
* The elements are ordered using their {@linkplain Comparable natural
* ordering}, or by a {@link Comparator} provided at set creation
* time, depending on which constructor is used.
*/
TreeSet的创建和常用方法
创建
TreeSet
的实例化与TreeMap
大同小异, 这里就只介绍构造方法
//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序
TreeSet();
//构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序
TreeSet(Collection<? extends E> c);
//构造一个新的空 TreeSet,它根据指定比较器进行排序
TreeSet(Comparator<? super E> comparator);
//构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet
TreeSet(SortedSet<E> s);
常用方法
方法 | 简述 |
---|---|
boolean add(E e) | 将指定的元素添加到此 set(如果该元素尚未存在于 set 中) |
void clear() | 移除此set 中的所有元素 |
boolean clone() | 如果此 set 包含指定的元素,则返回 true |
boolean isEmpty() | 如果此 set 不包含任何元素,则返回 true |
boolean remove() | 将指定的元素从 set 中移除(如果该元素存在于此 set 中) |
int size() | 返回 set 中的元素数(set 的容量) |
SortedSet subSet(E fromElement, E toElement) | 返回此 set 的部分视图,其元素从 fromElement (包括)到 toElement (不包括)。 |
SortedSet tailSet(E fromElement) | 返回此 set 的部分视图,其元素大于等于 fromElement |
文章到此结束, 感谢耐心阅读😸