自定義對映(Map)
阿新 • • 發佈:2019-01-03
通過學習自定義對映,瞭解對映的資料結構。
首先寫一個對映的介面,描述其具有的基本功能。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;
}
}