数据结构-映射

421 阅读4分钟

映射(Map)

映射是什么?

如果使用白话来说的话, 就是更具特定唯一的信息来找到对应的实体。
比如说我们查字典, 要查询的字能找到对应的解释。 比如我们的有身份证id就能查到对应的人信息。
比如我们有快递id就能知道当前快递在什么位置
等等, 都是通过映射的关系来进行实现。

映射结构特点

  • 以K,V键值对存储的数据
  • 根据K能快速的寻找到V
映射接口的设计

我们优先创建一个父类接口, 并设计两个子类一个是链表来实现, 一个是二分搜索树来实现。


public interface Map<K, V> {
    // 添加数据
    void add(K key, V value);
    // 删除数据
    V remove(K key);
    // 判断K是否存在
    boolean contains(K key);
    // 更具K获取value
    V get(K key);
    // 设置新的value
    void set(K key, V newValue);
    int getSize();
    boolean isEmpty();
}

基于链表实现映射数据结构

学习集合的时候, 我们也使用了链表进行集合的设计, 但是在设计映射对象的时候可能就无法满足了, 毕竟要K和V一对。 所以我们需要重新构建一下Node对象。并重新设计add和remove等方法。



public class LinkedListMap<K, V> implements Map<K, V> {

    // 之前写的链表只有一个元素是无法满足map的结构, 所以重新定义一下Node
    private class Node {
        public K key;
        public V value;
        public Node next;

        public Node() {
            this(null, null, null);
        }

        public Node(K key, V value, Node next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }

        @Override
        public String toString() {
            return key + " : " + value;
        }
    }


    private int size ;
    private Node dummyHead ; // 虚拟头结点

    public LinkedListMap() {
        dummyHead = new Node();
        size = 0;
    }

    /**
     * 更具K获取到具体的Node对象
     * @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;
    }

    @Override
    public void add(K key, V value) {
        Node cur = getNode(key);
        if (cur == null) {
            dummyHead.next = new Node(key, value, dummyHead.next);
            size ++ ;
        } else {
            // 已经存在了, 这里的话不抛出异常但是给个提示吧
            System.out.println("添加的key="+key+"已存在, 给你更新啦!");
            cur.value = value;
        }
    }

    @Override
    public void set(K key, V newValue) {
        Node cur = getNode(key);
        if (cur == null) {
            throw new IllegalArgumentException("当前key="+key+"不存在, 请检查是否拼写错误");
        }

        cur.value = newValue;
    }

    @Override
    public V remove(K key) {

        Node prev = dummyHead.next;
        while (prev.next != null) {
            if (prev.next.key.equals(key)) {
                break;
            }

            prev = prev.next;
        }

        if (prev.next != 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 cur = getNode(key);
        return cur == null ? null : cur.value;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }
}

接下来测试一下我们基于链表的映射数据结构把。


public static void main(String[] args) {
        String[] words = {"A", "B", "C", "D", "E", "A", "A", "A", "B", "B", "C", "C", "F", "F", "F", "K",};
        Map<String, Integer> map = new LinkedListMap<>();
        for (String word : words) {
            if (map.contains(word)) {
                Integer v = map.get(word) + 1;
                map.set(word, v);
            } else {
                map.add(word, 1);
            }
        }

        System.out.println("总共: " + map.getSize());

        System.out.println(map.get("A"));
        System.out.println(map.get("B"));
        System.out.println(map.get("C"));
        System.out.println(map.get("D"));
        System.out.println(map.get("E"));
        System.out.println(map.get("F"));
        System.out.println(map.get("K"));
}

基于二分搜索树实现映射数据结构

利用二分搜索树的话, 之前写的也是不能使用的。所以我们需要重新构建一个Node。



public class BSTMap<K extends Comparable<K>, V> implements Map<K, V> {


    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;
    }

    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.left, key);
        else
            return getNode(node.right, key);
    }

    @Override
    public void add(K key, V value) {
        root = add(root, key, value);
    }

    private Node add(Node node, K key, V value) {
        if (node == null) {
            Node n = new Node(key, value);
            size ++ ;
            return n;
        }

        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);
        } else {
            // 如果存在key则更新数据
            node.value = value;
        }

        return node;
    }


    private Node minimum(Node node) {
        if (node.left == null)
            return node ;
        return minimum(node.left);
    }

    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;
    }

    private Node remove(Node node, K key) {

        if (node.key.compareTo(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;
            }

            /***
             * 待删除节点左右子树都不为空的情况
             */
            // 1. 找到比待删除节点大的最小节点, 即待删除节点右子树中最小的节点(找到后继节点)
            // 2. 用后继节点顶替待删除节点的位置
            Node succeed = minimum(node.right);
            // 返回删除最小值后的一个新树, 最小值已经被我们记录住了, 然后设置左右子树
            succeed.right = removeMin(node.right);
            succeed.left = node.left;
            // 这里之所以没有size--, 是因为removeMin方法已经做了
            node.left = node.right = null;
            return succeed;
        } else if (node.key.compareTo(key) > 0) {
            node.right = remove(node.right, key);
            return node;
        } else {
            node.left = remove(node.left, key);
            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;
    }

    @Override
    public void set(K key, V newValue) {
        Node node = getNode(root, key);
        if (node == null)
            throw new IllegalArgumentException("key=" + key + " 不存在, 更新失败!");
        node.value = newValue;
    }

    @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 int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }
}

测试一下


public static void main(String[] args) {
        String[] words = {"A", "B", "C", "D", "E", "A", "A", "A", "B", "B", "C", "C", "F", "F", "F", "K",};
        Map<String, Integer> map = new BSTMap<>();

        for (String word : words) {
            if (map.contains(word)) {
                Integer v = map.get(word) + 1;
                map.set(word, v);
            } else {
                map.add(word, 1);
            }
        }

        System.out.println("总共: " + map.getSize());

        System.out.println(map.get("A"));
        System.out.println(map.get("B"));
        System.out.println(map.get("C"));
        System.out.println(map.get("D"));
        System.out.println(map.get("E"));
        System.out.println(map.get("F"));
        System.out.println(map.get("K"));

        Integer v = map.remove("A");
        System.out.println("A " + v);
        System.out.println(map.get("A"));
        System.out.println(map.get("K"));
}

avatar