數據結構之集合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)。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM