数据结构05——集合与映射
一、集合和映射的概念
对于集合,我们只要学过编程的人一般都是了解的,在编程中,我们常常需要集中存放多个数据。从传统意义上来讲,数组是我们的一个很好的选择,但前提是我们必须事先已经明确知道我们将要保存的对象的数量。一旦在数组初始化时指定了这个数组长度,这个数组长度就是不可变的,如果我们需要保存一个可以动态增长的数据(在编译时无法确定具体的数量),java的集合类就是一个很好的设计方案了。这是我们从编程方面所了解的一种集合概念。在这篇文章中,我们将自己去手写这样的一种数据结构,这样的数据结构将会基于我们前面文章所提到的一些数据结构。
对于映射,在数学里面,它只是一个术语,就是指两个元素的集之间元素相互“对应”的关系。其实在我们数据结构中,也是类似这样的一种概念。例如:身份证——人、单词——解释、数据库id——数据信息等等。后面我们将手写实现这样的一种数据结构。
二、集合的接口定义以及实现
对于集合的一些定义,我们一般也都了解,例如集合一般都不会存放重复的元素,因此这就让它有了去重的功用了。
public interface Set<E> { void add(E e);//不能添加重复元素 boolean contains(E e); void remove(E e); int getSize(); boolean isEmpty(); }
集合基于BST的实现
package com.zfy.setandmap; import com.zfy.bst.BST; /* * 这里是基于之前的二分搜索树实现的, * 1.因为集合具有去重的功用,说明它是可以有比较的功能的,所以这里实现了Comparable类, * 2.因为集合是可以存各种的数据类型,因此这里设计为泛型 * */ public class BSTSet<E extends Comparable<E>> implements Set<E> { private BST<E> bst; //所有的BSTSet都是基于BST的 public BSTSet() { bst = new BST<>(); } @Override public void add(E e) { bst.add(e); } @Override public boolean contains(E e) { return bst.contains(e); } @Override public void remove(E e) { bst.remove(e); } @Override public int getSize() { return bst.getSize(); } @Override public boolean isEmpty() { return bst.isEmpty(); } }
Set基于链表的实现,这里的链表是基于我们前面说的链表,对于其代码这里就不再贴了,大家可以去看前面的文章数据结构03——链表这篇文章。
package com.zfy.setandmap; import com.zfy.linkedlist.LinkedList; public class LinkedListSet<E> implements Set<E> { private LinkedList<E> list; public LinkedListSet() { list = new LinkedList<>(); } @Override public void add(E e) { //不能添加重复 if (!list.contains(e)) { list.addFirst(e); } } @Override public boolean contains(E e) { return list.contains(e); } @Override public void remove(E e) { list.removeElement(e); } @Override public int getSize() { return list.getSize(); } @Override public boolean isEmpty() { return list.isEmpty(); } }
三、映射(Map)接口的定义及实现
映射的一些概念:1. 映射是存储(键,值)数据对的数据结构(Key, Value),2.根据键(Key),寻找值(Value)
package com.zfy.setandmap; public interface Map<K, V> { void add(K key, V value); V remove(K key); boolean contains(K key); V get(K key); void set(K key, V newValue); int getSize(); boolean isEmpty(); }
Map的实现,基于BST的实现
package com.zfy.setandmap; /* * 因为这里是基于二分搜索树实现的,所以这里的K是需要可比较的,因此实现了Comparable * */ public class BSTMap<K extends Comparable<K>, V> implements Map<K, V> { private class Node { public K key; public V value; public Node left, right; public Node(K key, V value) { this.key = key; this.value = value; left = null; right = null; } } private Node root; private int size; public BSTMap() { root = null; size = 0; } // 向二分搜索树中添加新的元素(key, value) @Override public void add(K key, V value) { root = add(root, key, value); } // 向以node为根的二分搜索树中插入元素(key, value),递归算法 // 返回插入新节点后二分搜索树的根 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 // key.compareTo(node.key) == 0 node.value = value; return node; } // 返回以node为根节点的二分搜索树中,key所在的节点 @SuppressWarnings("unused") private Node getNode(Node node, K key) { if (node == null) { return null; } if (key.compareTo(node.key) < 0) { return getNode(node.left, key); } else if (key.compareTo(node.key) > 0) { return getNode(node.right, key); } else { return node; } } @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; } @Override public int getSize() { return size; } @Override public boolean isEmpty() { return size == 0; } // 从二分搜索树中删除键为key的节点 @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; } } // 返回以node为根的二分搜索树的最小值所在的节点 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; } }
基于链表实现的Map
package com.zfy.setandmap; public class LinkedListMap<K, V> implements Map<K, V> { /* * 因为前面的链表只能承载一个元素e,因此对于这里的映射类,我们需要重新重新实现Node了 * */ 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; public LinkedListMap(){ dummyHead = new Node(); size = 0; } //传来一个k的值,返回这个k所对应的节点的引用,后面的增删改查都将借助于这个方法 private Node getNode(K key){ Node cur = dummyHead.next;//cur对应dummyHead的next节点,就是第一个元素 //循环遍历判断cur是否包含传入的key,如果包含则返回cur,否则继续遍历 while (cur != null) { if (cur.equals(key)) { return cur; } cur = cur.next; } return null; } @Override public void add(K key, V value) { Node node = getNode(key);//首先调用这个方法,然后判断返回后的当前节点中是否包含添加所传入的key键所对应的数据,如果没有则添加,有则覆盖 if (node == null) { dummyHead.next = new Node(key, value, dummyHead.next); size ++; }else { node.value = value; } } //此方法发与之前的类似 @Override public V remove(K key) { Node prev = dummyHead; while (prev.next != null) { if (prev.next.key.equals(key)) { break; } prev = prev.next; } if (prev != null) { Node delNode = prev.next; prev.next = delNode.next; delNode.next = null; size --; return delNode.value; } return null; } @Override public boolean contains(K key) { return getNode(key) != null; } @Override public V get(K key) { Node node = getNode(key);//先调用getNode,返回所对应的节点,然后对其进行判断 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 + " doesn't exist!"); }else { node.value = newValue; } } @Override public int getSize() { return size; } @Override public boolean isEmpty() { return size == 0; } }
最后语:不积跬步,无以至千里;不积小流,无以成江海。对于知识总要温故,这样才能知新!
参考:bobobo老师的玩转数据结构
版权声明:尊重博主原创文章,转载请注明出处 https://www.cnblogs.com/hsdy