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

本文主要包括以下内容:

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

Set集合的基本概念

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

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

Set集合的基本操作

  1. 插入
  2. 删除
  3. Set是否为空
  4. Set是否包含某个元素
  5. Set元素个数

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

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

Set集合的BST实现和链表实现

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();
    }
}

链表实现Set

下面链表实现的Set,使用到了以前介绍的链表实现类 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和的性能差异:

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,只要和856作比较,不需要和链表一样最坏的情况需要和每个元素进行比较。而这个路径也就是二分搜索树的高度。下面用一个表格来对比二分搜索树实现的Set和链表实现的Set的时间复杂度:

操作LinkedListSetBSTSet
addO(n)O(h)
containsO(n)O(h)
removeO(n)O(h)

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

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

层数该层的节点数
0层1
1层2
2层4
3层8
4层16
h-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)

操作LinkedListSetBSTSet
addO(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 老师的视频《算法大神带你玩转数据结构 从入门到精通》
有需要的同学可以看看, 真心不错. 墙裂推荐… 最好能加上自己的思考和理解.


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

本文相关源代码

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiclaim

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值