数据结构与算法(九)Set集合和BinarySearchTree的时间复杂度分析

xiaoxiao2021-02-28  47

本文主要包括以下内容:

Set集合的基本概念Set集合的基本操作Set集合的BST实现和LinkedList实现Set集合两种实现方式的时间复杂度分析

Set集合的基本概念

Set集合是对数学中集合的抽象,Set集合有两个特性:

Set集合里没有重复元素Set集合是无序集合

Set集合的基本操作

插入删除Set是否为空Set是否包含某个元素Set元素个数

可以将以上几个操作定义一下几个方法

public interface Set<T> { void add(T e); boolean contains(T e); void remove(T e); boolean isEmpty(); int size(); }

Set集合的BST实现和LinkedList实现

Set集合底层我们可以使用顺序的线性表、链表、二叉树来实现。下面分别使用二分搜索树和链表来实现下Set集合

二分搜索树实现Set

二分搜索树实现Set使用到了前面介绍的二分搜索树的实现类 BST.java

关于二分搜索树,有需要的可以查看 数据结构与算法(八)二分搜索树(Binary Search Tree)

public class BSTSet<T extends Comparable<T>> implements Set<T> { private BST<T> bst; public BSTSet() { bst = new BST<>(); } @Override public void add(T e) { bst.add(e); } @Override public boolean contains(T e) { return bst.contains(e); } @Override public void remove(T e) { bst.contains(e); } @Override public boolean isEmpty() { return bst.isEmpty(); } @Override public int size() { return bst.size(); } }

链表实现二分搜索树

链表实现的二分搜索树,使用到了以前介绍的链表实现类 LinkedList.java

关于链表的内容,有需要的可以查看 数据结构与算法(二)线性表之链式存储和LinkedList

public class LinkedListSet<T> implements Set<T> { private LinkedList<T> linkedList; public LinkedListSet() { linkedList = new LinkedList<>(); } @Override public void add(T e) { if (!linkedList.contains(e)) { linkedList.addFirst(e); } } @Override public boolean contains(T e) { return linkedList.contains(e); } @Override public void remove(T e) { linkedList.remove(e); } @Override public boolean isEmpty() { return linkedList.isEmpty(); } @Override public int size() { return linkedList.size(); } }

利用Set的特性:不允许元素重复。我们可以使用Set集合来统计下两本英文原著双城记(A Tale of Two Cities)和傲慢与偏见(Pride and Prejudice)的词汇量

Pride and Prejudice Total words: 125901 Total different words: 6530 A Tale of Two Cities Total words: 141489 Total different words: 9944

从上面的数据可以看出,傲慢与偏见词汇量6530,双城记为9944,。这个统计只是简单的统计,比如单词的时态,单复数等都当做一个新单词。

Set集合两种实现方式的时间复杂度分析

我们来对比下基于二分搜索树和链表来实现的Set和的性能差异:

pride-and-prejudice.txt Total words: 125901 Total different words: 6530 pride-and-prejudice.txt Total words: 125901 Total different words: 6530 BSTSet Time: 0.121546597 LinkedListSet Time: 2.122136759

根据上面的统计数据可以看出,BSTSet比LinkedListSet快20倍左右。

我们知道链表的插入操作的时间复杂度是O(1),但是实现Set集合需要先判断集合中是否存在(contains),所以总的下来插入的操作为O(n)

那么二分搜索树的插入时间复杂度呢? 假如我们往以下一个二分搜索树插入元素 7,插入路径如下图所示:

根据插入路径我们知道,插入元素7,只要和8、5、6作比较,不需要和链表一样最坏的情况需要和每个元素进行比较。而这个路径也就是二分搜索树的高度。下面用一个表格来对比二分搜索树实现的Set和链表实现的Set的时间复杂度:

操作LinkedListSetBSTSetaddO(n)O(h)containsO(n)O(h)removeO(n)O(h)

h就是二分搜索树的高度,那么高度 h 和二分搜索树节点数 n 的关系是什么呢?

分析下满的二叉树的情况就知道了节点数量和二叉树高度的的关系了。

层数该层的节点数0层11层22层43层84层16h-1层2^(h-1)

那么一个h层的满二叉树总共有多少节点呢?就是每层的元素个数相加:

n = 20+21+23+24+…+2^(h-1) = 2^h - 1

用对数表示就是:h = log(n+1)

用大O表示法就是: O(h) = O(log n)

操作LinkedListSetBSTSetaddO(n)O(log n)containsO(n)O(log n)removeO(n)O(log n)

对比下 log(n) 和 n 的差距

nlog(n)差距1644 倍102410100倍100w205w倍

这也就是为什么上面的的BSTSet和LinkedListSet快那么多的原因。

我们上面对于二分搜索树的分析是基于满二叉树的,也就是最好的情况下,但是我们知道二分搜索树在最坏的情况会退化成链表,这就需要用到平衡二叉树如 AVL树、红黑树,就算是最坏的情况也能保证二分搜索树的不会退化成链表,保持树大致的平衡,后面会介绍到平衡二叉树相关的内容。

Reference

本文主要内容和大纲是学习了慕课网 liuyubobobo 老师的视频《算法大神带你玩转数据结构 从入门到精通》 有需要的同学可以看看, 真心不错. 墙裂推荐… 最好能加上自己的思考和理解.


下面是我的公众号,干货文章不错过,有需要的可以关注下,有任何问题可以联系我:

本文相关源代码

转载请注明原文地址: https://www.6miu.com/read-2613924.html

最新回复(0)