数据结构之集合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)。

 

posted on 2020-03-14 17:23  别先生  阅读(1190)  评论(0编辑  收藏  举报