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)。