映射Map
Map 在有些编程语言中也叫做字典
Map接口
public interface Map<K,V> {
/**
* 存入键值对
* @param key
* @param value
* @return 返回之前key对应的value
*/
V put(K key,V value);
V get(K key);
V remove(K key);
int size();
boolean containsKey(K key);
boolean containsValue(V value);
boolean isEmpty();
void clear();
void traversal(Visitor<K,V> visitor);
public static abstract class Visitor<K,V>{
public abstract void visit(K key,V value);
}
}
TreeMap
TreeMap本身其实是一棵红黑树,只不过他的节点中存储了Key和Value,其实现与红黑树基本相同。
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Queue;
public class TreeMap<K,V> {
private Node<K,V> root;
private int size=0;
private Comparator<K> comparator;//比较器
public TreeMap(){}//不传入比较器的构造
public TreeMap(Comparator<K> comparator){//传入比较器的构造
this.comparator=comparator;
}
@Override
public V put(K key, V value) {
//如果是第一个元素
if(null==root){
root=createNode(key,value,null);
size++;
afterAdd(root);//后续操作
return null;
}
//不是第一个节点
Node<K,V> node=root;
Node<K,V> parent=null;
int cmp=0;
while(node!=null){
cmp=compare(key,node.key);
parent=node;
if(cmp<0){ //进入左侧节点
node=node.left;
}else if(cmp>0){//进入右侧节点
node=node.right;
}else{//相等覆盖
node.key=key;
V oldValue=node.value;
node.value=value;
afterAdd(node);
return oldValue;
}
}
Node<K,V> newNode=createNode(key,value, parent);
//如果左侧节点为空,放置于左节点
if(cmp<0)
parent.left = newNode;
else
parent.right = newNode;
size++;
afterAdd(newNode);
return null;//返回之前的节点,此处为空
}
@Override
public V get(K key) {//获取键值对
Node<K,V> node=node(key);
return node==null?null:node.value;
}
@Override
public V remove(K key) {
return remove(node(key));
}
@Override
public int size() {
return size;
}
@Override
public boolean containsKey(K key) {
return node(key)!=null;
}
@Override
public boolean containsValue(V value) {
Queue<Node<K,V>> queue=new LinkedList<>();
queue.offer(root);
Node<K,V> cuNode=null;
while(!queue.isEmpty()){
cuNode=queue.poll();//出队
if(value== cuNode.value)
return true;
if(null!=cuNode.left)
queue.offer(cuNode.left);
if(null!=cuNode.right)
queue.offer(cuNode.right);
}
return false;
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public void clear() {
root=null;
size=0;
}
@Override
public void traversal(Visitor<K, V> visitor) {
if(null==visitor)return;
inorderTraversal(root,visitor);
}
public static final boolean BLACK=false;
public static final boolean RED=true;
//树节点
private static class Node<K,V>{
K key;
V value;
boolean color=RED;
Node<K,V> left;
Node<K,V> right;
Node<K,V> parent;
public Node(){}
public Node(K key,V value,Node<K,V> parent){
this.key=key;
this.value=value;
this.parent=parent;
}
public boolean isLeaf(){
return left==null&&right==null;
}
public boolean hasTwoChildren(){
return left!=null&&right!=null;
}
//判断当前节点是否是父节点的左节点
public boolean isLeftChild(){
return parent!=null&&this==parent.left;
}
//判断当前节点是否是父节点的左节点
public boolean isRightChild(){
return parent!=null&&this==parent.right;
}
/**
* 获取兄弟节点
* @return
*/
public Node<K,V> sibling(){
if(isLeftChild()){
return parent.right;
}else if(isRightChild()){
return parent.left;
}
//否则说明没有父节点
return null;
}
}
private Node<K,V> createNode(K key,V value, Node<K,V> parent){
return new Node(key,value,parent);
}
/**比较方法中使用Key进行比较
* @param k1 键1
* @param k2 键2
* @return -1代表k1大,0代表一样大,1代表k2大
*/
private int compare(K k1,K k2){
//调用自身比较器的方法
if(null!=comparator)
return comparator.compare(k1,k2);
//强转为Comparable类型
return ((Comparable<K>)k1).compareTo(k2);
}
private void afterAdd(Node<K,V> node) {
Node<K,V> parent=node.parent;
if(parent==null){//0.node是根节点
black(node);//根节点必须是黑色
return;
}
if(isBlack(parent))//1.父节点为黑色则无需处理
return;
Node<K,V> uncle=parent.sibling();//获取node叔父节点
Node<K,V> grand=parent.parent;
if(isRed(uncle)){//2.uncle是红色节点的情况
black(parent);//将父节点和叔父节点涂黑,独立
black(uncle);
afterAdd(red(grand));//将祖父节点向上合并
return;
}
//3.uncle节点是黑色
if(parent.isLeftChild()){//L
if(node.isLeftChild()){//LL
red(grand);
black(parent);
rotateRight(grand);
}else{//LR
red(grand);
black(node);
rotateLeft(parent);
rotateRight(grand);
}
}else{//R
if(node.isLeftChild()){//RL
red(grand);
black(node);
rotateRight(parent);
rotateLeft(grand);
}else{//RR
red(grand);
black(parent);
rotateLeft(grand);
}
}
}
//右旋
private void rotateRight(Node<K,V> grand){
Node<K,V> parent=grand.left;
Node<K,V> child=parent.right;//移交给grand节点的子节点
grand.left=child;
parent.right=grand;
//后续操作
afterRotate(grand,parent,child);
}
//左旋
private void rotateLeft(Node<K,V> grand){
Node<K,V> parent=grand.right;
Node<K,V> child=parent.left;
grand.right=child;
parent.left=grand;
//后续操作
afterRotate(grand,parent,child);
}
void afterRotate(Node<K,V> grand, Node<K,V> parent, Node<K,V> child){
parent.parent=grand.parent;
if(grand.isRightChild())
grand.parent.right=parent;
else if(grand.isLeftChild())
grand.parent.left=parent;
else //grand是根节点
root=parent;
if(child!=null)
child.parent=grand;
grand.parent=parent;
}
private void afterRemove(Node<K,V> node, Node<K,V> replacement) {
if(isRed(node))//如果删除的是红色节点
return;
if(isRed(replacement)){//如果用于取代的是红色节点
black(replacement);//将节点染黑
return;
}
//此时删除的是黑色叶子节点,会导致下溢(4阶B树的节点元素范围[1,3]
//情况分类
//0.删除的节点是根节点,无需操作
if(node.parent==null)return;
//1.兄弟节点是黑色且有红色子节点(此时parent可能为黑色,但是操作相同):对parent进行旋转,从兄弟节点分一个节点到待删除节点
Node<K,V> parent=node.parent;
//叶子节点删除后为空,利用空判断之前是左是右
boolean left=parent.left==null||node.isLeftChild();//可能是递归进入的该函数,判断条件需加上是否是左节点
Node<K,V> sibling=left?parent.right:parent.left;//不能用node判断,parent已经不指向它了
if(!left){//被删除节点在右边,兄弟节点在左边
if(isRed(sibling)){//兄弟节点是红色
black(sibling);//将兄弟节点染黑
red(parent);//parent节点染红
rotateRight(parent);//parent右旋转
sibling=parent.left;//更换兄弟
}
//兄弟节点一定是黑色
if(isBlack(sibling.left)&&isBlack(sibling.right)){//兄弟节点为黑色且没有红色子节点
//此时父节点需要向下和兄弟节点合并
boolean parentBlack=isBlack(parent);
black(parent);
red(sibling);
if(parentBlack)//若parent之前为黑色,则也产生了下溢
afterRemove(parent,null);
}else{
if(isBlack(sibling.left)){//兄弟节点没有红色节点可用
rotateLeft(sibling);
sibling=parent.left;
}
color(sibling,colorOf(parent));
black(parent);
black(sibling.left);
rotateRight(parent);
}
}else{
if(isRed(sibling)){//兄弟节点是红色
black(sibling);//将兄弟节点染黑
red(parent);//parent节点染红
rotateLeft(parent);//parent右旋转
sibling=parent.right;//更换兄弟
}
//兄弟节点一定是黑色
if(isBlack(sibling.left)&&isBlack(sibling.right)){//兄弟节点为黑色且没有红色子节点
//此时父节点需要向下和兄弟节点合并
boolean parentBlack=isBlack(parent);
black(parent);
red(sibling);
if(parentBlack)//若parent之前为黑色,则也产生了下溢
afterRemove(parent,null);
}else{
if(isBlack(sibling.right)){//兄弟节点没有红色节点可用
rotateLeft(sibling);
sibling=parent.right;
}
color(sibling,colorOf(parent));
black(parent);
black(sibling.right);
rotateLeft(parent);
}
}
//2.兄弟节点是黑色且没有红色子节点:将父节点染黑,兄弟节点染红,父节点与兄弟节点合并
//(这样会带来黑色节点的子节点都是黑色节点的情况),若此时parent也是黑色节点,则会产生parent节点也下溢
//这种情况下,我们可以看作对parent节点进行了删除处理
//3.兄弟节点是红色,将兄弟节点染黑,父节点染红,情况就和上一样了
}
/**
* 染色方法
* @param node
* @param color
* @return
*/
private Node<K,V> color(Node<K,V> node, boolean color){
if(node==null)return node;
node.color=color;
return node;
}
/**
* 染红
* @param node
* @return
*/
private Node<K,V> red(Node<K,V> node){
node.color= RED;
return node;
}
/**
* 染黑
* @param node
* @return
*/
private Node<K,V> black(Node<K,V> node){
node.color= BLACK;
return node;
}
/**
* 获取颜色
* @param node
* @return
*/
private boolean colorOf(Node<K,V> node){
return node==null?BLACK:node.color;
}
/**
* 判断是否是红色
* @param node
* @return
*/
private boolean isRed(Node<K,V> node){
return colorOf(node)==RED;
}
/**
* 判断是否是黑色
* @param node
* @return
*/
private boolean isBlack(Node<K,V> node){
return colorOf(node)==BLACK;
}
/**
* 删除对印的节点
* @param node
*/
private V remove(Node<K,V> node){
if(null==node)return null;
size--;
V oldValue=null;
if(node.hasTwoChildren()) {//有两个叶子节点,使用后继节点代替
Node<K,V> successor = successor(node);//后继节点
oldValue=node.value;//保存待删除节点元素值
node.key = successor.key;//替换元素值
node.value=successor.value;
node=successor;//将node指向待删除元素
}
//对指向的节点进行删除
if(null==oldValue)oldValue=node.value;//若上方未保存值,则此处保存
Node<K,V> replacement=node.left!=null?node.left:node.right;
if(replacement==null) {//没有子节点,是叶子节点
if (root == node) {//node是根节点
root = null;
afterRemove(node,null);
}
else {
if (node == node.parent.left)
node.parent.left = null;
else
node.parent.right = null;
afterRemove(node,null);
}
}else{//非叶子节点
replacement.parent=node.parent;//更改父节点
if(root==node) {//如果node是根节点
root = replacement;
}else if(node==node.parent.left)
node.parent.left=replacement;
else
node.parent.right=replacement;
afterRemove(node,replacement);
}
return oldValue;//返回之前的元素值
}
/**
* 查找到元素为key的节点
* @param key
*/
private Node<K,V> node(K key){
Node<K,V> node=root;
while(null!=node){
int cmp=compare(key,node.key);
if(cmp==0)return node;
else if(cmp<0)node=node.left;
else node=node.right;
}
return null;
}
/**
* 获取该节点的后继节点,即中序遍历的后节点
* @param node
* @return
*/
private Node<K,V> successor(Node<K,V> node){
Node<K,V> prev=node.right;
//当右节点不为空,寻找右子树中最左端的节点
if(null!=prev){
while(prev.left!=null){
prev=prev.left;
}
return prev;
}
//此时右节点为空,后继节点为父节点(祖父..),且父节点的左节点是当前节点
//去掉中间的作为父节点右节点的节点
while(null!=node.parent&&node==node.parent.right){
node=node.parent;
}
//null==node
//node==node.parent.right
return node.parent;
}
private void inorderTraversal(Node<K,V> node,Visitor<K,V> visitor){
if(null==node)return;
inorderTraversal(node.left,visitor);
// System.out.print(node.key+":"+node.value+" ");
visitor.visit(node.key,node.value);
inorderTraversal(node.right,visitor);
}
private void levelOrderTraversal(Node<K,V> node){
Queue<Node<K,V>> queue=new LinkedList<>();
queue.offer(node);
Node<K,V> cuNode=null;
while(!queue.isEmpty()){
cuNode=queue.poll();//出队
System.out.print(node.key+":"+node.value+" ");//访问
if(null!=cuNode.left)
queue.offer(cuNode.left);
if(null!=cuNode.right)
queue.offer(cuNode.right);
}
}
}
┌──────────────────────────────H:34-BLACK─────────────────────────────┐
│ │
┌───────D:27-BLACK──────┐ ┌────────────────────P:69-BLACK────────────────────┐
│ │ │ │
┌─B:20-BLACK─┐ ┌─F:53-BLACK─┐ ┌────────L:51-RED───────┐ ┌───────────T:49-RED───────────┐
│ │ │ │ │ │ │ │
A:60-BLACK C:21-BLACK E:62-BLACK G:85-BLACK ┌─J:99-BLACK─┐ ┌─N:32-BLACK─┐ ┌─R:18-BLACK─┐ ┌───────X:64-BLACK───────┐
│ │ │ │ │ │ │ │
I:9-BLACK K:67-BLACK M:72-BLACK O:57-BLACK Q:7-BLACK S:53-BLACK ┌─V:23-RED─┐ ┌──o:23-RED───┐
│ │ │ │
U:45-BLACK W:72-BLACK ┌─Z:95-BLACK─┐ world:2-BLACK─┐
│ │ │
Y:33-RED hello:3-RED 变态:6-RED
Set与Map的关系
只需将Map节点中的value始终置空,即只是用Key和Value中的一个,Map就可以转变为Set。