数据结构与算法之映射

(一) 定义

映射Map: 存储(键, 值)数据对的数据结构(Key, Value), 我们可以根据键(Key)来寻找值(Value), 且key在映射中是唯一的.实现映射的方式多种多样, 在JAVA语言表现中, 会将映射定义成一个接口, 提供映射对应的操作的抽象方法.

/**
 * 自定义映射抽象类Map
 * 
 * @author Administrator
 *
 * @param <K>
 * @param <V>
 */
public interface Map<K, V> {

	/**
	 * 向映射中添加键值对数据(Key键不允许重复)
	 * 
	 * @param key
	 * @param value
	 */
	void add(K key, V value);
	
	/**
	 * 从映射中删除键为key的数据
	 * 
	 * @param key
	 * @return
	 */
	V remove(K key);
	
	/**
	 * 映射中是否存在key
	 * 
	 * @param key
	 * @return
	 */
	boolean contains(K key);
	
	/**
	 * 从映射中取出key键 对应的value值
	 * 
	 * @param key
	 * @return
	 */
	V get(K key);
	
	/**
	 * 修改映射中key对应的value值
	 * 
	 * @param key
	 * @param value
	 */
	void set(K key, V value);
	
	/**
	 * 映射中键值对个数
	 * 
	 * @return
	 */
	int getSize();
	
	/**
	 * 映射是否为空
	 * 
	 * @return
	 */
	boolean isEmpty();
}

(二) 自定义映射

1.基于链表的映射实现
public class LinkedListMap<K, V> implements Map<K, V> {

	/**
	 * 节点内部类: 可以单独定义出来, 但用户无需关注映射底层实现, 因此定义成LinkedListMap的内部类
	 * 
	 * @author Administrator
	 *
	 */
	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;
		}
		
		@SuppressWarnings("unused")
		public Node(K key) {
			this(key, null, null);
		}
		
		public Node() {
			this(null, null, null);
		}
		
		@Override
		public String toString() {
			return key.toString() + " : " + value.toString();
		}
	}
	
	/**
	 * 虚拟头部节点: 存储的数据为Null, 下一个节点next也为Null
	 */
	private Node dummyHead;

	/**
	 * 映射键值对个数
	 */
	private int size;
	
	public LinkedListMap() {
		this.dummyHead = new Node();
		this.size = 0;
	}
	
	@Override
	public int getSize() {
		return this.size;
	}

	@Override
	public boolean isEmpty() {
		return this.size == 0;
	}
	
	/**
	 * 获取key对应的结点
	 * 
	 * @param key
	 * @return
	 */
	private Node getNode(K key) {
		Node cur = this.dummyHead.next;
		while(cur != null) {
			if (cur.key.equals(key)) {
				return cur;
			}
			cur = cur.next;
		}
		return null;
	}

	@Override
	public boolean contains(K key) {
		return getNode(key) != null;
	}
	
	@Override
	public V get(K key) {
		Node res = getNode(key);
		return res == null ? null : res.value;
	}
	
	@Override
	public void add(K key, V value) {
		Node node = getNode(key);
		if (node == null) {
			// key键在映射中不存在, 添加新的结点
			dummyHead.next = new Node(key, value, dummyHead.next);
			size++;
		} else {
			// key键在映射中存在, 修改key所在的结点对应的值为 value 
			node.value = value;
		}
	}
	
	@Override
	public void set(K key, V value) {
		Node node = getNode(key);
		if (node == null) {
			throw new IllegalArgumentException(key + " doesn't exist!");
		} 
		node.value = value;
	}

	@Override
	public V remove(K key) {
		Node prev = this.dummyHead;
		while (prev.next != null) {
			if (prev.next.key.equals(key)) {
				V value = prev.next.value;
				prev.next = prev.next.next;
				size--;
				return value;
			}
			prev = prev.next;
		}
		return null;
	}

}
2.基于二分搜索树的映射实现
public class BSTMap<K extends Comparable<K>, V> implements Map<K, V> {

	/**
	 * 节点内部类: 可以单独定义出来, 但用户无需关注映射底层实现, 因此定义成BSTMap的内部类
	 * 
	 * @author Administrator
	 *
	 */
	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() {
		this.root = null;
		this.size = 0;
	}
	
	@Override
	public int getSize() {
		return this.size;
	}

	@Override
	public boolean isEmpty() {
		return this.size == 0;
	}
	
	/**
	 * 向映射中添加键值对key-value(Key键不允许重复)
	 */
	@Override
	public void add(K key, V value) {
		this.root = add(this.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);
		}
		// 二分搜索树中存在插入key, 修改key对应的value
		if (node.key.compareTo(key) == 0) {
			node.value = value;
		} else if (node.key.compareTo(key) < 0) {
			node.right = add(node.right, key, value); 
		} else if (node.key.compareTo(key) > 0) {
			node.left = add(node.left, key, value); 
		}
		return node;
	}
	
	/**
	 * 返回以node为根结点定的二分搜索树中, key所对应的结点
	 * 
	 * @param key
	 * @return
	 */
	private Node getNode(Node node, K key) {
		if (node == null) {
			return null;
		} 
		if (node.key.compareTo(key) == 0) {
			return node;
		} else if (node.key.compareTo(key) < 0) {
			return getNode(node.right, key);
		} else {
			return getNode(node.left, key);
		}
	} 
	
	@Override
	public boolean contains(K key) {
		return getNode(root, key) != null;
	}
	
	@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 + " doesn't exist!");
		}
		node.value = newValue;
	}
	
	/**
	 * 返回以node为根的二分搜索树的最小值所在的结点
	 * 
	 * @param node
	 * @return
	 */
	private Node minimum(Node node) {
		if (node.left != null) {
			return node.left;
		}
		return minimum(node.left);
	}
	
	/**
	 * 向以node为根的二分搜索树中删除最小元素, 返回删除最小元素节点后二分搜索树的跟
	 * 
	 * @param node
	 * @return
	 */
	private Node removeMin(Node node) {
		if (node.left == null) {
			Node delNode = node.right;
			node.right = null;
			size--;
			return delNode;
		}
		node.left = removeMin(node.left);
		return node;
	}
	
	/**
	 * 从映射中删除键为key的数据
	 */
	@Override 
	public V remove(K key) {
		Node node = getNode(root, key);
		if (node != null) {
			root = remove(root, key);
			return node.value;
		}
		return null;
	}
	
	/**
	 * 删除以node为根的二分搜索树中键为key的结点, 返回删除节点后新的二分搜索树的跟
	 * 
	 * @param node
	 * @param key
	 * @return
	 */
	private Node remove(Node node, K key) {
		if (node == null) {
			return null;
		} 
		if (node.key.compareTo(key) == 0) {
			
			if (node.left == null) {
				Node delNode = node.right;
				node.right = null;
				size--;
				return delNode;
			}
			
			if (node.right == null) {
				Node delNode = node.left;
				node.left = null;
				size--;
				return delNode;
			}
			Node replaceNode = minimum(node.right);
			replaceNode.right = removeMin(node.right);
			replaceNode.left = node.left;
			return replaceNode;
		} else if (node.key.compareTo(key) < 0) {
			node.right = remove(node.right, key);
		} else {
			node.left = remove(node.left, key);
		}
		return node;
	}
}

(三) 时间复杂度分析

add(e)方法为了保证映射存储的key不重复, 内部调用了getNode(k) 方法

 LinkedListMapBSTMap平均最差(链表)
add(k, v)O(n)O(h)O(logn)O(n)
remove(k)O(n)O(h)O(logn)O(n)
contains(k)O(n)O(h)O(logn)O(n)
get(k)O(n)O(h)O(logn)O(n)
set(k, v)O(n)O(h)O(logn)O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值