1. 程式人生 > >自定義對映(Map)

自定義對映(Map)

通過學習自定義對映,瞭解對映的資料結構。

 

本篇分別以連結串列的方式和以二分搜尋樹的方式實現了對映。

 

首先寫一個對映的介面,描述其具有的基本功能。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;
	}

}