• 数据结构之集合Set


    1、高层的数据结构,集合Set和映射Map,什么是高层的数据结构呢,比如说是栈和队列,这种数据结构更像是先定义好了使用接口,有了这些使用接口,包括数据结构本身所维持的一些性质,可以很方便的放入到一些应用中,但是底层实现可以多种多样的,比如栈和队列,底层实现既可以是动态数据,也可以是链表。

      集合就是承载元素的容器,集合Set中有一个重要的特性,就是每个元素在集合中只能存在一次,可以快速帮助去重工作,去重就是去除重复的元素,让所有的元素只保留一份。

    2、基于二分搜索树实现的Set集合。代码,如下所示:

    首先定义一个接口,然后分别使用二分搜索树的方式和链表的方式实现集合的功能。

     1 package com.set;
     2 
     3 /**
     4  * @ProjectName: dataConstruct
     5  * @Package: com.set
     6  * @ClassName: Set
     7  * @Author: biehl
     8  * @Description: ${description}
     9  * @Date: 2020/3/14 10:41
    10  * @Version: 1.0
    11  */
    12 public interface Set<E> {
    13 
    14 
    15     /**
    16      * Set集合的新增
    17      *
    18      * @param e
    19      */
    20     public void add(E e);
    21 
    22     /**
    23      * 删除集合的元素
    24      *
    25      * @param e
    26      */
    27     public void remove(E e);
    28 
    29     /**
    30      * 判断是否包含某个元素
    31      *
    32      * @param e
    33      * @return
    34      */
    35     public boolean contains(E e);
    36 
    37     /**
    38      * 获取集合的个数
    39      *
    40      * @return
    41      */
    42     public int getSize();
    43 
    44     /**
    45      * 判断集合是否为空
    46      *
    47      * @return
    48      */
    49     public boolean isEmpty();
    50 }

    使用二分搜索树的方式实现,代码如下所示:

     1 package com.set;
     2 
     3 import com.tree.BinarySearchTree;
     4 
     5 /**
     6  * 基于二分搜索树实现的Set集合
     7  *
     8  * @ProjectName: dataConstruct
     9  * @Package: com.set
    10  * @ClassName: BSTSet
    11  * @Author: biehl
    12  * @Description: ${description}
    13  * @Date: 2020/3/14 10:44
    14  * @Version: 1.0
    15  */
    16 public class BSTSet<E extends Comparable<E>> implements Set<E> {
    17 
    18     // 定义二分搜索树
    19     private BinarySearchTree<E> binarySearchTree;
    20 
    21     /**
    22      *
    23      */
    24     public BSTSet() {
    25         // 无参构造函数,创建二分搜索树对象
    26         binarySearchTree = new BinarySearchTree<E>();
    27     }
    28 
    29     @Override
    30     public void add(E e) {
    31         // 对于重复的元素,不进行任何操作
    32         binarySearchTree.add(e);
    33     }
    34 
    35     @Override
    36     public void remove(E e) {
    37         binarySearchTree.remove(e);
    38     }
    39 
    40     @Override
    41     public boolean contains(E e) {
    42         return binarySearchTree.contains(e);
    43     }
    44 
    45     @Override
    46     public int getSize() {
    47         return binarySearchTree.size();
    48     }
    49 
    50     @Override
    51     public boolean isEmpty() {
    52         return binarySearchTree.isEmpty();
    53     }
    54 
    55     public static void main(String[] args) {
    56         BSTSet<String> bstSet = new BSTSet<String>();
    57         // 集合Set的新增操作
    58         for (int i = 0; i < 100; i++) {
    59             bstSet.add(i + "");
    60         }
    61 
    62         for (int i = 0; i < bstSet.getSize(); i++) {
    63             System.out.println(bstSet.toString());
    64         }
    65 
    66         // 集合Set的删除操作
    67         bstSet.remove(0 + "");
    68 
    69         // 集合Set的是否包含某个元素
    70         boolean contains = bstSet.contains(0 + "");
    71         System.out.println(contains);
    72 
    73         // 集合Set的大小
    74         System.out.println(bstSet.getSize());
    75 
    76         // 判断集合Set是否为空
    77         System.out.println(bstSet.isEmpty());
    78     }
    79 
    80 }

    3、二分搜索树和链表都是属于动态数据结构。二分搜索树和链表的数据都是存储到Node节点中的。

     1 package com.set;
     2 
     3 import com.linkedlist.LinkedList;
     4 
     5 /**
     6  * @ProjectName: dataConstruct
     7  * @Package: com.set
     8  * @ClassName: LinkedListSet
     9  * @Author: biehl
    10  * @Description: ${description}
    11  * @Date: 2020/3/14 11:54
    12  * @Version: 1.0
    13  */
    14 public class LinkedListSet<E> implements Set<E> {
    15 
    16     private LinkedList<E> linkedList;
    17 
    18     /**
    19      * 无参构造函数,对linkedList进行初始化
    20      */
    21     public LinkedListSet() {
    22         linkedList = new LinkedList<E>();
    23     }
    24 
    25     @Override
    26     public void add(E e) {
    27         // 避免将重复的元素添加进去
    28         if (!linkedList.contains(e)) {
    29             linkedList.addFirst(e);
    30         }
    31     }
    32 
    33     @Override
    34     public void remove(E e) {
    35         linkedList.removeElement(e);
    36     }
    37 
    38     @Override
    39     public boolean contains(E e) {
    40         return linkedList.contains(e);
    41     }
    42 
    43     @Override
    44     public int getSize() {
    45         return linkedList.getSize();
    46     }
    47 
    48     @Override
    49     public boolean isEmpty() {
    50         return linkedList.isEmpty();
    51     }
    52 
    53     public static void main(String[] args) {
    54         LinkedListSet<Integer> linkedListSet = new LinkedListSet<Integer>();
    55         // 基于链表实现的集合的新增
    56         for (int i = 0; i < 100; i++) {
    57             linkedListSet.add(i);
    58         }
    59 
    60         // 集合Set的删除操作
    61         linkedListSet.remove(0);
    62 
    63         // 集合Set的是否包含某个元素
    64         boolean contains = linkedListSet.contains(0);
    65         System.out.println(contains);
    66 
    67         // 集合Set的大小
    68         System.out.println(linkedListSet.getSize());
    69 
    70         // 判断集合Set是否为空
    71         System.out.println(linkedListSet.isEmpty());
    72 
    73     }
    74 }

    4、基于链表的集合实现的性能,慢与基于二分搜索树的集合实现。

    集合Set的时间复杂度分析。

      1)、增加add。
        方式一,基于链表实现的LinkedListSet,本来在链表中添加一个元素时间复杂度是O(1)的,但是对于集合Set来说,需要去除重复元素,所以对于链表需要先查询一遍,查询的时间复杂度O(1),所以整体上,基于LinkedListSet方式实现的的新增操作,时间复杂度是O(1)。
        方式二,基于二分搜索树的实现的BSTSet,新增操作,每次新增都可以排除一半元素,因为大于根节点去右子树,小于根节点去左子树,新增操作从根节点向叶子节点出发,一层一层的向下走,经历的节点是二分搜索树的深度,新增操作、查询元素、删除元素都是这个思路,那么平均时间复杂度的是O(h)或者O(logn),其中h是二分搜索树的深度。最差的效果是时间复杂度的是O(n)。解决这个问题可以使用平衡二叉树。

      2)、查询contails。
        方式一,基于链表实现的LinkedListSet,查询操作的时间复杂度是O(1),因为要把所有的元素遍历。
        方式二,基于二分搜索树的实现的BSTSet,那么平均时间复杂度的是O(h)或者O(logn),其中h是二分搜索树的深度。最差的效果是时间复杂度的是O(n)。解决这个问题可以使用平衡二叉树。

      3)、删除remove。
        方式一,基于链表实现的LinkedListSet,删除操作的时间复杂度是O(1),因为需要先找到待删除元素的前面哪一个节点,再将这个元素删除。
        方式二,基于二分搜索树的实现的BSTSet,那么平均时间复杂度的是O(h)或者O(logn),其中h是二分搜索树的深度。最差的效果是时间复杂度的是O(n)。解决这个问题可以使用平衡二叉树。
      总结,那么n和h的比较是怎么样的呢,对于一棵满二叉树来说,如果一共有h层的话,节点个数一共是2的h次方减一个节点。那么2^h-1 = n,则h = log2(n + 1),log以2为底的n + 1的对数。h = O(logn),此时不管以那个数字为底的,直接简写成h = O(logn)。

  • 相关阅读:
    IOS 面试题(不断更新...)
    IOS 数字日期转化为字符串
    C#汉字生成简拼
    ObjectiveC 深浅拷贝
    数组遍历方法forEach 和 map 的区别
    COJ1174(Shining Gems)
    POJ1062(昂贵的聘礼)
    HDOJ1879(继续畅通工程)
    最短路径经典题集(转载)
    HDOJ1863(畅通工程)
  • 原文地址:https://www.cnblogs.com/biehongli/p/12487717.html
Copyright © 2020-2023  润新知