版权声明:转载请注明出处: https://blog.csdn.net/qq_34774655/article/details/85337439
通过学习自定义映射,了解映射的数据结构。
首先写一个映射的接口,描述其具有的基本功能。Map.java
然后写一个接口的实现类:
其中,方式一: 用的是链表的方式。 LinkedListMap.java
方式二:用的是二分搜索树的方式。 BSTMap.java
方式一与方式二的时间复杂度分析:
其中: 字母h代表树的深度。
|
LinkedListMap |
BSTMap 平均情况 最坏情况 |
||
add |
O(n) |
O(h) |
O(log n) |
O(n) |
contains |
O(n) |
O(h) |
O(log n) |
O(n) |
remove |
O(n) |
O(h) |
O(log n) |
O(n) |
set |
O(n) |
O(h) |
O(log n) |
O(n) |
get |
O(n) |
O(h) |
O(log n) |
O(n) |
此外还分:有序映射、无序映射、多重映射。
有序映射:元素中的键具有顺序性。(基于搜索树的实现)
无序映射:元素中的键没有顺序性。(基于哈希表的实现)
多重映射:元素中的键可以重复。
Map.java
package SetAndMap;
public interface Map<K,V>{
void add(K key,V value);//键不可重复
boolean contains(K key);
V remove(K key);
V get(K key);
void set(K key, V newValue);
int getSize();
boolean isEmpty();
}
方式一:
package SetAndMap;
public class LinkedListMap<K,V> implements Map<K,V> {
/**
* 内部类
* 描述节点信息
* @author xiaohua
*
*/
private class Node{
public K key;
public V value;
public Node next;
public Node(K key,V value,Node next) {
this.key=key;
this.value=value;
this.next=next;
}
public Node(K key,V value) {
this(key,value,null);
}
public Node() {
this(null,null,null);
}
@Override
public String toString() {
return key.toString()+" : "+value.toString();
}
}
private Node dummyHead;//虚拟头结点
private int size;//映射的大小
/**
* 无参构造函数
* 初始化一个虚拟头节点,它的key、value都为null
*/
public LinkedListMap() {
dummyHead=new Node();
size=0;
}
/**
* 内部的一个辅助函数,以下的添加、修改等方法都依赖于此。
* 作用:获取当前键所对应的节点。若无,则返回null。
* @param key
* @return
*/
private Node getNode(K key) {
Node cur=dummyHead.next;
while(cur!=null) {
if(cur.key.equals(key)) {
return cur;
}
cur=cur.next;
}
return null;
}
/**
* 往映射中添加元素。若已存在该键,则修改value值
*/
@Override
public void add(K key, V value) {
Node node =getNode(key);
if(node==null) {
dummyHead.next=new Node(key,value,dummyHead.next);
size++;
}else {
node.value=value;
}
}
/**
* 判断映射中是否已存在该键
*/
@Override
public boolean contains(K key) {
Node node =getNode(key);
return node!=null;
}
/**
* 获取键所对应的value
*/
@Override
public V get(K key) {
Node node =getNode(key);
return node==null? null:node.value;
}
/**
* 修改键所对应的值
*/
@Override
public void set(K key, V newValue) {
Node node =getNode(key);
if(node == null)
throw new IllegalArgumentException(key + " 不存在");
node.value=newValue;
}
/**
* 删除指定的键,并返回键所对应的值
*/
@Override
public V remove(K key) {
Node pre=dummyHead;
while(pre.next !=null) {
if(pre.next.key.equals(key)) {
break;
}
pre=pre.next;
}
if(pre.next!=null) {
Node delNode=pre.next;
pre.next=delNode.next;
delNode.next=null;
size--;
return delNode.value;
}
return null;
}
/**
* 返回映射的大小
*/
@Override
public int getSize() {
return size;
}
/**
* 判断映射是否为空
*/
@Override
public boolean isEmpty() {
return size==0;
}
}
方式二:
package SetAndMap;
public class BSTMap<K extends Comparable<K>,V> implements Map<K,V> {
/**
* 内部类
* 描述节点信息
* @author xiaohua
*
*/
private class Node{
public K key;
public V value;
public Node left;
public Node right;
public Node(K key,V value) {
this.key=key;
this.value=value;
this.left=null;
this.right=null;
}
}
private Node root;//根节点
private int size;//映射的大小
/**
* 无参构造函数
*
*/
public BSTMap() {
root=null;
size=0;
}
/**
* 内部的一个辅助函数,以下的获取、判断、修改等方法都依赖于此。
* 作用:返回以node为根节点的二分搜索树中,key所在的节点
* @param
* @return
*/
private Node getNode(Node node,K key) {
if(node==null) {
return null;
}
if(key.equals(node.key)) {
return node;
}else if(key.compareTo(node.key)<0) {
return getNode(node.left,key);
}else{
return getNode(node.right ,key);
}
}
/**
* 往映射中添加元素。若已存在该键,则修改value值
*/
@Override
public void add(K key, V value) {
root=add(root,key ,value);
}
/**
* 向以node为根的二分搜索树中插入元素(key, value),递归算法
*返回插入新节点后二分搜索树的根
* @param node
* @param key
* @param value
* @return
*/
private Node add(Node node, K key, V value) {
if(node==null) {
size++;
return new Node(key,value);
}
if(key.compareTo(node.key )<0 ) {
node.left=add(node.left,key,value);
}else if(key.compareTo(node.key )>0) {
node.right=add(node.right,key,value);
}else {
node.value=value;//若已存在键,则修改相应的值
}
return node;
}
/**
* 判断映射中是否已存在该键
*/
@Override
public boolean contains(K key) {
Node node =getNode(root,key);
return node!=null;
}
/**
* 获取键所对应的value
*/
@Override
public V get(K key) {
Node node =getNode(root,key);
return node==null? null:node.value;
}
/**
* 修改键所对应的值
*/
@Override
public void set(K key, V newValue) {
Node node =getNode(root,key);
if(node == null)
throw new IllegalArgumentException(key + " 不存在");
node.value=newValue;
}
/**
* 返回以node为根的二分搜索树的最小值所在的节点
* @param node
* @return
*/
private Node minimum(Node node){
if(node.left == null)
return node;
return minimum(node.left);
}
/**
* 删除掉以node为根的二分搜索树中的最小节点
* 返回删除节点后新的二分搜索树的根
*/
private Node removeMin(Node node){
if(node.left == null){
Node rightNode = node.right;
node.right = null;
size --;
return rightNode;
}
node.left = removeMin(node.left);
return node;
}
/**
* 删除指定的键,并返回键所对应的值
*/
@Override
public V remove(K key) {
Node node=getNode(root,key);
if(node!=null) {
root=remove(root,key);
return node.value;
}
return null;
}
private Node remove(Node node, K key) {
if(node==null) {
return null;
}
if(key.compareTo(node.key )<0) {
node.left=remove(node.left ,key);
return node;
}else if(key.compareTo(node.key )<0){
node.right=remove(node.right ,key);
return node;
}else {// key.compareTo(node.key) == 0
// 待删除节点左子树为空的情况
if(node.left==null) {
Node rightNode=node.right;
node.right=null;
size--;
return rightNode;
}
// 待删除节点右子树为空的情况
if(node.right==null) {
Node leftNode=node.left;
node.left=null;
size--;
return leftNode;
}
// 待删除节点左右子树均不为空的情况
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置
Node successor=minimum(node.right);
successor.right=removeMin(node.right);
successor.left=node.left;
node.left=node.right=null;
return successor;
}
}
/**
* 返回映射的大小
*/
@Override
public int getSize() {
return size;
}
/**
* 判断映射是否为空
*/
@Override
public boolean isEmpty() {
return size==0;
}
}