数据结构-Set集合(一)Set集合介绍、优缺点

Set集合

定义

  • Set集合是一种不重复元素 的抽象数据结构,是一个包含一系列不重复元素集合

数据结构原理

  • 通过哈希表(HashTable)实现
    • Hash函数定位存储位置

      • 每个元素通过哈希函数映射到一个哈希值,用于快速定位内存中存储位置(bucket桶)
    • 解决哈希冲突

      • 如果出现哈希冲突,通过开放地址法链表法解决冲突
    • 判断数据已存在

      • 插入时,再判断如果已经存在相同数据,不进行保存;否则进行保存
    • 动态扩容

      • 负载因子 (元素数量/桶数量)超过阈值0.75),自动扩容 并进行重新哈希(rehash)

引申问题:为什么需要动态扩容?

  • 核心目的:降低哈希冲突概率防止操作效率下降

  • 原理:

    • 哈希表底层是一个数组,元素会通过哈希函数映射到数组某个位置(即桶)
    • 随着元素的的增加,哈希冲突会越来越频繁即不同元素映射到同一个桶的概率越来越高),而哈希冲突会导致性能下降,所以需要扩容来降低哈希冲突概率
    • 随着元素的增加,操作效率也会趋向于线性时间(O(1)会退化到O(n)),即效率会越来越低

引申问题:负载因子的意义

  • 定义负载因子=元素个数桶的数量 \text{负载因子} = \frac{\text{元素个数}}{\text{桶的数量}} 负载因子=桶的数量元素个数

  • 作用

    • 体现哈希表的"拥挤程度"
    • 哈希表的阈值0.75,如果超过阈值,说明哈希表太满了,此时哈希冲突概率很高,需要扩容

引申问题:为什么扩容后需要rehash?

  • 原因

    • 哈希值一般是取模运算来得出内存位置(桶位置)的,而扩容一般是将内容大小翻倍(桶的数量翻倍)
    • 所以扩容后,再进行取模运算时,原来的哈希值对应的内存位置(桶位置)已经发生了变化,需要重新进行哈希(即重新计算每个元素的内存位置/桶位置
  • rehash执行过程

    1. 创建更大的数组,用于对应更多的桶
    2. 遍历原哈希表里的所有元素
    3. 每个元素重新计算哈希存到新的桶

操作类型

核心操作(基础操作)

  • 添加
  • 删除
  • 查询

派生操作(可通过核心操作实现)

  • 遍历
  • 并集:所有元素的合集
  • 交集:两个集合共有元素
  • 差集:属于a不属于b的集合,即a-b
  • 子集判断:b集合包含所有a集合的元素,a为b的子集

特点

  • 数据不重复

    • 元素唯一性 是set集合最大的特点,即不会有数据重复 ,该特征可以用来去重
  • 无序性

    • Set集合中的数据是无序 的,不能 通过索引 访问
      • 若需要有序集合可以使用OrderedSet(如 collections.OrderedDict 的变种)或者SortedSet

优点

  • 查找效率高

    • 基于哈希表实现,查询效率非常高
  • 自动去重

    • 插入时自动去重,自动去除重复元素
  • 集合运算

    • 便于并集、交集、差集等数学集合运算

缺点

  • 哈希冲突

    • 实现需要处理哈希冲突
    • 当随着负载因子的提高,哈希冲突会变得越来越频繁,为了应对这一点,还需要在负载因子超过阈值后进行rehash
  • 无序

    • 不能保证元素的插入顺序
    • 也无法按索引访问元素
  • 内存占用较大

    • 哈希表结构通常比数组或者列表占用更大的内存

使用场景

  • 数据去重

    • 利用Set元素唯一特性,去除集合中的重复元素
  • 快速查找

    • 使用set来判断集合中是否包含这个元素
  • 集合运算

    • 对两个集合进行交集、并集、差集的运算
  • 判断唯一性

    • 通过Set元素唯一特性,统计唯一元素的个数

实际代码案例

场景一:列表去重

说明:

将一个包含重复元素的列表转换为不重复的集合。

Java 示例:
java 复制代码
import java.util.*;

public class SetDeduplication {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Alice", "David", "Bob");
        Set<String> uniqueNames = new HashSet<>(names);

        System.out.println("去重后的名字集合: " + uniqueNames);
    }
}

场景二:判断元素是否存在(快速查找)

说明:

使用 Set 来判断某个元素是否在黑名单中。

Java 示例:
java 复制代码
import java.util.*;

public class BlacklistCheck {
    public static void main(String[] args) {
        Set<String> blacklist = new HashSet<>(Arrays.asList("192.168.1.1", "10.0.0.1"));

        String ip = "192.168.1.1";
        if (blacklist.contains(ip)) {
            System.out.println("该 IP 在黑名单中,拒绝访问!");
        } else {
            System.out.println("该 IP 允许访问。");
        }
    }
}

场景三:集合运算(交集、并集、差集)

说明:

找出两个集合的交集、并集和差集。

Java 示例:
java 复制代码
import java.util.*;

public class SetOperations {
    public static void main(String[] args) {
        Set<Integer> setA = new HashSet<>(Arrays.asList(1, 2, 3, 4));
        Set<Integer> setB = new HashSet<>(Arrays.asList(3, 4, 5, 6));

        // 交集
        Set<Integer> intersection = new HashSet<>(setA);
        intersection.retainAll(setB);
        System.out.println("交集: " + intersection);

        // 并集
        Set<Integer> union = new HashSet<>(setA);
        union.addAll(setB);
        System.out.println("并集: " + union);

        // 差集
        Set<Integer> difference = new HashSet<>(setA);
        difference.removeAll(setB);
        System.out.println("差集 (A - B): " + difference);
    }
}

场景四:判断集合关系(子集、超集)

Java 示例:
java 复制代码
import java.util.*;

public class SetRelations {
    public static void main(String[] args) {
        Set<String> setA = new HashSet<>(Arrays.asList("a", "b"));
        Set<String> setB = new HashSet<>(Arrays.asList("a", "b", "c"));

        System.out.println("A 是 B 的子集: " + setB.containsAll(setA));
        System.out.println("B 是 A 的超集: " + setB.containsAll(setA));
    }
}

场景五:统计唯一访问用户(UV)

Java 示例:
java 复制代码
import java.util.*;

public class UniqueVisitors {
    public static void main(String[] args) {
        List<String> userVisits = Arrays.asList("user1", "user2", "user1", "user3", "user2");
        Set<String> uniqueUsers = new HashSet<>(userVisits);

        System.out.println("独立访客数(UV): " + uniqueUsers.size());
    }
}
相关推荐
冒泡的肥皂2 分钟前
数据库最近学到的小知识(一
数据库·后端·架构
AI 嗯啦12 分钟前
机械学习--逻辑回归
算法·机器学习·逻辑回归
山烛24 分钟前
逻辑回归详解:从数学原理到实际应用
python·算法·机器学习·逻辑回归
程序视点26 分钟前
电脑硬件检测必备!图吧工具箱11年免费良心软件!100+免费工具合集
前端·windows·后端
weixin_4374999229 分钟前
【PHP属性详解:从基础到只读的完全指南】
开发语言·php
枫叶落雨22230 分钟前
Hutool 的 WordTree(敏感词检测)
java·开发语言
IguoChan33 分钟前
话一话职场PUA
后端
Layflok41 分钟前
《黑马笔记》 --- C++ 提高编程
开发语言·c++·stl
陈随易1 小时前
国产之光,把AI融入到语言级别的编程语言-MoonBit
前端·后端·程序员