• Map(映射)介绍(基于链表实现Map和基于二分搜索树实现Map)


    1、什么是Map(映射)

    存储( 键,值) 数据对的数据结构(Key,Value)

     根据键(Key),寻找值(Value)

    非常容易使用链表或者二分搜索树实现。

    2、基于链表实现Map

    public class LinkedListMap<K,V>  implements IMap<K,V>{
    
        private class  Node<K,V>{
            K key;
            V value;
            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(K key){
                this(key, null, 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;
        }
    
    
        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;
            }
        }
    
        public V remove(K key) {
            Node preNode = dummyHead;
            while (preNode.next != null){
                if(preNode.next.key.equals(key)){
                    break;
                }else {
                    preNode = preNode.next;
                }
            }
            if(preNode.next != null){
                Node delNode = preNode.next;
                preNode.next = delNode.next;
                delNode.next = null;
                size --;
                return (V)delNode.value;
            }
            return  null;
        }
    
        private Node getNode(K key){
            Node cur = dummyHead.next;
            while (cur != null){
                if(cur.key.equals(key)){
                    return cur;
                }else {
                    cur = cur.next;
                }
            }
            return  null;
        }
    
        public boolean contains(K key) {
          return  getNode(key) != null;
    
        }
    
        public V get(K key) {
            Node node = getNode(key);
            return  node == null ? null : (V)node.value;
        }
    
        public void set(K key, V newValue) {
            Node node = getNode(key);
            if(node == null){
                throw  new IllegalArgumentException(key + " doesn't exist!");
            }
    
            node.value = newValue;
    
        }
    
        public int getSize() {
            return size;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
      
    }
    

      

    测试:

    public static void main(String[] args) {
            //数据准备
            ArrayList<String> list = new ArrayList<String>();
            String[] arr = {"张三", "李四","王五", "赵六","张三丰","李思明","王老五","赵明"};
            for(int i = 0; i < 10; i++){
                for(String str : arr){
                    String key = str +i;
                    list.add(key);
                }
            }
            for(int i = 0; i < 5000; i++){
                for(String str : arr){
                    String key = str +i;
                    list.add(key);
                }
            }
            System.out.println("准备的数据数量=" + list.size());
    
            LinkedListMap<String, Integer> map = new LinkedListMap();
            for(String key: list){
                Integer value = map.get(key);
                if(value == null){
                    map.add(key, 1);
                }else {
                    map.add(key, value +1);
                }
            }
    
            System.out.println("Map size: " + map.getSize());
            String key = "张三0";
            System.out.println(key + "出现的次数:" + map.get(key));
    
        }
    

      

    测试结果:

    准备的数据数量=40080
    Map size: 40000
    张三0出现的次数:2

    3、基于二分搜索树实现Map

    public class BinarySearchTreeMap<K extends Comparable<K>, V>  implements  IMap<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;
                left = null;
                right = null;
            }
        }
    
        private  Node root;
        private  int size;
    
        //向二分搜索树中添加新的元素key,value
        public void add(K key, V value){
            root = add(root, key, value);
        }
    
        //向以node为根的二分搜索树种插入元素key,value,递归算法
        public 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;
    
        }
    
    
    
        //寻找最小节点
        public K minimum(){
            if(size == 0){
                throw  new IllegalArgumentException("Bst is empty");
            }
            return  minimum(root).key;
    
        }
    
        //返回以Node为根的二分搜索树的最小值的节点
        public Node minimum(Node node){
            if(node.left == null){
                return  node;
            }
            return  minimum(node.left);
        }
    
        // 从二分搜索树中删除最小值所在节点, 返回最小值
        public K removeMin(){
            K key = minimum();
            root = removeMin(root);//注意这里,我们从root为根的树中删除掉了最小值,返回删除后的树的根节点,这个
            //根节点就是新的root
            return key;
        }
    
    
        // 删除掉以node为根的二分搜索树中的最小节点
        // 返回删除节点后新的二分搜索树的根
        private Node removeMin(Node node){
            // 递归的终止条件,当前节点没有左子树了,那么就是最小节点了
            // 如果是最小节点,我们要做的是删除当前节点,但是当前节点很可能是有右子树的
            // 我们先把该节点的右子树节点保存,然后就删除掉该右子树节点,最后把右子树节点返回即可
            if(node.left == null){
                Node rightNode = node.right;
                node.right = null;//左节点为空了,让右子树也为空,相当于脱离了树
                size --;
                return rightNode;//返回右子树是为了后面的绑定操作
            }
            // 没有递归到底的情况,那么就递归调用其左子树,这个调用的过程会返回被删除节点的右子树,
            //将返回的右子树重新绑定到上一层的node的左节点上就相当于彻底删除了那个元素
            node.left = removeMin(node.left);
            return node; // 删除后,根节点依然是node,返回即可
        }
    
        //从二分搜索树中删除元素为e的节点
        public V remove(K key) {
            Node node = getNode(key);
            if(node != null){
                root = remove(root, key);
                return  node.value;
            }
            return  null;
    
        }
    
        //删除以node为根的二分搜索树中值为键为key的节点,递归算法
        // 返回要删除节点后新的二分搜索树的根
        private Node remove(Node node,K key) {
    
            if (node == null) {
                return null;
            }
    
            if (key.compareTo(node.key) < 0) {
                node.left = remove(node.left, key);
            }
    
            if (key.compareTo(node.key) > 0) {
                node.right = remove(node.right, key);
            } else {
    
                // 待删除节点左子树为空
                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;
    
            }
    
            return null;
    
        }
    
    
        public boolean contains(K key) {
    
            return getNode(key) != null;
        }
    
        public V get(K key) {
            Node node =  getNode(key);
            return node != null  ? node.value : null;
        }
    
        public void set(K key, V newValue) {
            Node node = getNode(key);
            if(node == null){
                throw  new IllegalArgumentException(key + " doesn't exist");
            }
            node.value = newValue;
        }
        private Node getNode(K key){
            return  getNode(root, key);
        }
    
        private Node getNode(Node node, K key){
            if(node == null){
                return  null;
            }
            if(key.compareTo(node.key) == 0){
                return  node;
            } else if(key.compareTo(node.key) < 0){
               return getNode(node.left, key);
            }else { //if(key.compareTo(node.key) > 0)
                return getNode(node.right, key);
            }
    
        }
    
        public int getSize() {
            return size;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
       
    }
    

      

    测试:

     public static void main(String[] args) {
            //数据准备
            ArrayList<String> list = new ArrayList<String>();
            String[] arr = {"张三", "李四","王五", "赵六","张三丰","李思明","王老五","赵明"};
            for(int i = 0; i < 10; i++){
                for(String str : arr){
                    String key = str +i;
                    list.add(key);
                }
            }
            for(int i = 0; i < 5000; i++){
                for(String str : arr){
                    String key = str +i;
                    list.add(key);
                }
            }
            System.out.println("准备的数据数量=" + list.size());
    
            BinarySearchTreeMap<String, Integer> map = new BinarySearchTreeMap();
            for(String key: list){
                Integer value = map.get(key);
                if(value == null){
                    map.add(key, 1);
                }else {
                    map.add(key, value +1);
                }
            }
    
            System.out.println("Map size: " + map.getSize());
            String key = "张三0";
            System.out.println(key + "出现的次数:" + map.get(key));
    
        }
    

      

    输出结果:(和基于链表的Map结果一致,但是速度明显更快)

    准备的数据数量=40080
    Map size: 40000
    张三0出现的次数:2

    5、两种Map进行比较

    public class TwoMapTest {
    
        private static  double testMap(IMap<String, Integer> map, ArrayList<String> list){
            long startTime = System.nanoTime();
    
            for(String key: list){
                Integer value = map.get(key);
                if(value == null){
                    map.add(key, 1);
                }else {
                    map.add(key, value +1);
                }
            }
            System.out.println("Map size: " + map.getSize());
            String key = "张三0";
            System.out.println(key + "出现的次数:" + map.get(key));
            long endTme = System.nanoTime();
            return  (endTme - startTime) / 1000000000.0;
        }
    
        public static void main(String[] args) {
    
            ArrayList<String> list = new ArrayList<String>();
            String[] arr = {"张三", "李四","王五", "赵六","张三丰","李思明","王老五","赵明"};
            for(int i = 0; i < 10; i++){
                for(String str : arr){
                    String key = str +i;
                    list.add(key);
                }
            }
            for(int i = 0; i < 5000; i++){
                for(String str : arr){
                    String key = str +i;
                    list.add(key);
                }
            }
            System.out.println("准备的数据数量=" + list.size());
    
    
            LinkedListMap<String, Integer> map1 = new LinkedListMap<String, Integer>();
            double time1 =  testMap(map1, list);
            System.out.println("链表Map花费" + time1);
            System.out.println("-------------------------------");
            BinarySearchTreeMap<String, Integer> map2 = new BinarySearchTreeMap<String, Integer>();
            double time2 =   testMap(map2, list);
            System.out.println("testMapSet花费" + time2);
    
        }
    }
    

      

    输出结果如下:

    准备的数据数量=40080
    Map size: 40000
    张三0出现的次数:2
    链表Map花费12.458235521
    -------------------------------
    Map size: 40000
    张三0出现的次数:2
    testMapSet花费0.03265158  

    输出的结果一致,BinarySearchTreeMap的时间花费明显更少。

    6、Map的时间复杂度分析

     BinarySearchTreeMap最差情况为O(n),为了解决这个问题,后面引入了平衡二叉树

    7、有序Map和无序Map

    有序Map中的键具有顺序性,如基于搜索树实现的Map

    无序Map中的键没有顺序性, 如这里基于链表的Map

    8、多重Map

    多重Map中的键可以重复。

    作者:Work Hard Work Smart
    出处:http://www.cnblogs.com/linlf03/
    欢迎任何形式的转载,未经作者同意,请保留此段声明!

  • 相关阅读:
    winform 通过 windows api 实现本机两个exe 消息推送
    winform ListView控件 绘制item和subitems颜色
    C# Windows Service创建和安装
    C# 解析xml文件(带命名空间 xmlns和 xmlns:xsi)
    H5+CSS3 实现分页功能
    C# HashTable Dictionary ConcurrentDictionary HashSet
    使用泛型返回类
    WPF的XAML注意点
    Http监听HttpListener接收请求的Nancy框架
    获取Windows后台进程
  • 原文地址:https://www.cnblogs.com/linlf03/p/14398773.html
Copyright © 2020-2023  润新知