C# 位图算法

比如大量整数的快速查找,利用位操作来表示整数集合,因此操作速度非常快。

普通查找算法

首先,我们看看普通的查找算法,比如使用List<int>HashSet<int>来查找整数。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        List<int> numbers = new List<int>();
        Random random = new Random();
        for (int i = 0; i < 1000000; i++)
        {
            numbers.Add(random.Next(0, 1000000));
        }

        // 查找数字
        int target = 500000;
        bool exists = numbers.Contains(target);
        Console.WriteLine("普通算法 - 目标存在: " + exists);
    }
}

位图算法

使用位图算法实现同样的查找。假设整数的范围在0999999之间,我们可以使用一个位图来表示这些整数

using System;

public class Program
{
    public static void Main()
    {
        const int size = 1000000;
        byte[] bitmap = new byte[size / 8]; // 使用位图存储数据
        Random random = new Random();

        // 填充位图
        for (int i = 0; i < size; i++)
        {
            int num = random.Next(0, size);
            SetBit(bitmap, num);
        }

        // 查找数字
        int target = 500000;
        bool exists = GetBit(bitmap, target);
        Console.WriteLine("位图算法 - 目标存在: " + exists);
    }

    private static void SetBit(byte[] bitmap, int num)
    {
        bitmap[num / 8] |= (byte)(1 << (num % 8));
    }

    private static bool GetBit(byte[] bitmap, int num)
    {
        return (bitmap[num / 8] & (1 << (num % 8))) != 0;
    }
}

如果没看懂,这里是SetBitGetBit 的实现细节

private static void SetBit(byte[] bitmap, int num)
{
    // 计算该数字应在 bitmap 数组中的哪个字节
    int byteIndex = num / 8;

    // 计算该数字在该字节中的哪个位(0-7)
    int bitIndex = num % 8;

    // 使用位运算将该位设置为1
    bitmap[byteIndex] |= (byte)(1 << bitIndex);
}

private static bool GetBit(byte[] bitmap, int num)
{
    // 计算该数字应在 bitmap 数组中的哪个字节
    int byteIndex = num / 8;

    // 计算该数字在该字节中的哪个位(0-7)
    int bitIndex = num % 8;

    // 检查该位是否为1
    return (bitmap[byteIndex] & (1 << bitIndex)) != 0;
}

假设我们要在位图中设置并检查数字13的存在性:

  1. 设置数字13的存在性:

    • byteIndex = 13 / 8 = 1 (数字13位于第1个字节)
    • bitIndex = 13 % 8 = 5 (数字13在第1个字节的第5位)
    • 1 << 5 结果是00100000,所以bitmap[1] |= 00100000会将第1个字节的第5位设置为1。
  2. 检查数字13是否存在:

    • 同样计算byteIndexbitIndex
    • 通过bitmap[1] & 00100000检查第5位是否为1,如果是1,说明数字13存在,返回true

性能对比与优势说明

  • 普通查找算法:

    • 优点: 实现简单,适用于查找少量数据或范围较大的数据。
    • 缺点 : 随着数据量增加,List<int>的查找复杂度为O(n),而HashSet<int>的查找复杂度为O(1),但它们都存在一定的内存和时间开销,特别是在处理非常大量数据时,性能会下降。
  • 位图算法:

    • 优点: 在整数范围已知且较小的情况下,查找、插入和删除操作的时间复杂度为O(1),非常高效。因为它直接通过位操作确定数据的存在性,内存占用也非常少。
    • 缺点: 位图算法仅适用于整数范围已知且范围相对较小的情况。如果整数范围很大,位图的大小会显著增加。

性能测试

假设我们在一个范围为0999999之间的整数集合中查找一个数字500000

  • 普通查找算法 :当使用List<int>时,查找的平均时间复杂度为O(n),对于大规模数据,性能较差。如果使用HashSet<int>,查找时间复杂度为O(1),但相对较高的内存占用和哈希冲突仍然可能影响性能。

  • 位图算法 :查找时间复杂度为O(1),内存占用极低(仅需1000000/8 = 125000字节,即约122KB),且查找速度非常快,无论数据量多大,查找性能几乎不受影响。

相关推荐
爱吃生蚝的于勒1 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
ChoSeitaku7 小时前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
Fuxiao___7 小时前
不使用递归的决策树生成算法
算法
我爱工作&工作love我7 小时前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子8 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower8 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯8 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui18 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展
一个不喜欢and不会代码的码农8 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
前端郭德纲8 小时前
浏览器是加载ES6模块的?
javascript·算法