散列表- 位图

散列表- 位图

1.1 位图的介绍

  • Bitmap 的基本原理就是用一个 bit 来标记某个元素对应的 Value,而 Key 即是该元素。由于采用一个bit 来存储一个数据,因此可以大大的节省空间

  • Java 中 int 类型占用 4 个字节,即 4 byte,又 1 byte = 8 bit,所以 一个 int 数字的表示大概如下
    在这里插入图片描述
    位图的实现

  • 1 . 初始化数组大小

    • 假设N就是位图中的最大值,那么数组的长度就应该是N / 32 + 1,等同于 (N + 1) >> 5
  • 2 . 加入元素

    • N >> 5 找到索引值
    • N % 32 余数就是索引值所在32位的具体位置,等同于N & 31
    • 让1 左移N &31位和数组索引位置值得二进制做个 | 运算
  • 3 . 删除元素

    • 删除元素和增加元素类似,在处理结果时让索引位置的值 & ~(1 << (N & 31))
      就把这个位置上设置成0了
  • 4 . 判断元素是否存在

    • 这个是和add差不多的操作,查看N的索引N的位置的这个值是否为1
      直接 (arr[val >> 5] & (1 << (val & 63))) != 0 即可

1.2 手动实现位图

package com.lagou.bitMap.entity;

/**
 * @author 云梦归遥
 * @date 2022/5/13 18:10
 * @description
 */
public class BitMap {
    private int[] map; // 存储数据的数组
    private int length = 1; // 数组的默认长度
    public BitMap(){
        this.map = new int[length];
    }

    // 扩容数组
    public void resize(){
        int[] newMap = new int[length];
        System.arraycopy(map, 0, newMap, 0, map.length);
        map = newMap;
        System.out.println("【位图扩容成功】");
    }

    // 增加元素
    public void add(int num){
        if (num > length * 32 - 1){
            // 进行扩容
            length = (num >> 5) + 1;
            resize();
        }
        map[num >> 5] |= 1 << ((num - (num >> 5) * 32) & 31);
    }

    // 删除元素
    public void delete(int num){
        map[num >> 5] &= ~(1 << ((num - (num >> 5) * 32) & 31));
    }

    // 查询
    public boolean select(int num){
        return (map[num >> 5] & (1 << ((num - (num >> 5) * 32) & 31))) != 0;
    }
}

进行测试

package com.lagou.bitMap.test;

import com.lagou.bitMap.entity.BitMap;

/**
 * @author 云梦归遥
 * @date 2022/5/13 18:26
 * @description
 */
public class BitMapTest {
    public static void main(String[] args) {
        BitMap bitMap = new BitMap();
        bitMap.add(1);
        bitMap.add(10);
        bitMap.add(30);
        bitMap.add(70);
        bitMap.add(90);
        System.out.println(bitMap.select(5)? "5 元素存在": "5 元素不存在");
        System.out.println(bitMap.select(10)? "10 元素存在": "10 元素不存在");
        System.out.println(bitMap.select(50)? "50 元素存在": "50 元素不存在");
        System.out.println(bitMap.select(90)? "90 元素存在": "90 元素不存在");
        bitMap.delete(10);
        System.out.println(bitMap.select(10)? "10 元素存在": "10 元素不存在");
    }
}

在这里插入图片描述

1.3 总结

JAVA中一个int占4字节,32位二进制数, 如果一个int整数32位二进制都表示一个数的话,那么一个整数能表示[0,31]。如果想表示[32,64]可以加入数组,让索引0位置的数表示[0,31],索引1表示[32,64],以此类推只要知道要存入的最大值,就可以实现一个比原来数组占用空间至少小32倍的数组 ,就是位图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是基于散列表技术的排序的C++代码实现: ```cpp #include <iostream> #include <fstream> #include <bitset> #include <algorithm> #include <vector> #include <thread> using namespace std; const int MAX_NUM = 10000000; // 散列表的最大长度 const int BUCKET_SIZE = 100000; // 散列表的桶的数量 const int BUFFER_SIZE = 1000000000; // 缓冲区的大小,单位为字节 const int THREAD_NUM = 4; // 线程数量 bitset<MAX_NUM> bit_map; // 位,用于判断某个数是否出现过 vector<int> bucket[BUCKET_SIZE]; // 散列表,每个桶存放一组数 char buffer[BUFFER_SIZE]; // 缓冲区,用于缓存内存中的数据 int buffer_pos = 0; // 缓冲区的当前位置 int last_num = 0; // 上一个读入的数 // 将一个数插入到散列表中 void insert_num(int num) { int pos = num % BUCKET_SIZE; // 计算桶的位置 bucket[pos].push_back(num); // 将数插入到桶中 bit_map.set(num, true); // 将位相应位置设为true } // 将散列表中的数据写入缓冲区中 void write_bucket_to_buffer() { for (int i = 0; i < BUCKET_SIZE; i++) { if (bucket[i].size() > 0) { int len = bucket[i].size(); char* data = reinterpret_cast<char*>(&bucket[i][0]); // 将vector中的数据转换为char数组 int size = len * sizeof(int); // 计算数据大小 memcpy(buffer + buffer_pos, data, size); // 将数据复制到缓冲区中 buffer_pos += size; // 更新缓冲区的当前位置 bucket[i].clear(); // 清空桶中的数据 } } } // 排序线程函数 void sort_thread(int start, int end) { for (int i = start; i < end; i++) { int pos = i % BUCKET_SIZE; sort(bucket[pos].begin(), bucket[pos].end()); // 对桶中的数据进行排序 } } // 将缓冲区中的数据写入硬盘中 void write_buffer_to_disk(int file_num) { char file_name[20]; sprintf(file_name, "file_%d.bin", file_num); // 文件名为file_{file_num}.bin ofstream out(file_name, ios::binary); out.write(buffer, buffer_pos); // 将缓冲区中的数据写入文件中 buffer_pos = 0; // 清空缓冲区 } // 处理文件中的数据 void process_file(const char* file_name) { ifstream in(file_name, ios::binary); while (in.peek() != EOF) { int num; in.read(reinterpret_cast<char*>(&num), sizeof(int)); // 从文件中读入一个数 if (!bit_map.test(num)) { // 如果这个数没有出现过,则插入到散列表中 insert_num(num); } if (buffer_pos >= BUFFER_SIZE) { // 如果缓冲区已经满了,则将散列表中的数据写入缓冲区中 write_bucket_to_buffer(); if (buffer_pos >= BUFFER_SIZE) { // 如果散列表中的数据写入缓冲区后仍然超过了缓冲区的大小,则写入硬盘中 write_buffer_to_disk(last_num / MAX_NUM); last_num += MAX_NUM; } } } write_bucket_to_buffer(); // 处理完文件中的所有数据后,将散列表中的数据写入缓冲区中 } // 读入一个文件,并将其中的数据插入到散列表中 void read_file(const char* file_name) { thread t[THREAD_NUM]; for (int i = 0; i < THREAD_NUM; i++) { int start = i * MAX_NUM / THREAD_NUM; int end = (i + 1) * MAX_NUM / THREAD_NUM; t[i] = thread(sort_thread, start, end); // 创建排序线程 } process_file(file_name); // 处理文件中的数据 for (int i = 0; i < THREAD_NUM; i++) { t[i].join(); // 等待排序线程结束 } write_buffer_to_disk(last_num / MAX_NUM); // 将缓冲区中的数据写入硬盘中 } // 合并多个文件中的数据,并将结果写入到输出文件中 void merge_files(const char* output_file) { ofstream out(output_file, ios::binary); int file_num = (last_num / MAX_NUM) + 1; vector<ifstream> files(file_num); vector<int> nums(file_num); for (int i = 0; i < file_num; i++) { char file_name[20]; sprintf(file_name, "file_%d.bin", i); // 文件名为file_{i}.bin files[i].open(file_name, ios::binary); files[i].read(reinterpret_cast<char*>(&nums[i]), sizeof(int)); // 从文件中读入一个数 } while (true) { int min_num = MAX_NUM + 1; int min_pos = -1; for (int i = 0; i < file_num; i++) { if (nums[i] < min_num) { // 找到最小的数 min_num = nums[i]; min_pos = i; } } if (min_pos == -1) { // 如果没有找到最小的数,则退出循环 break; } out.write(reinterpret_cast<char*>(&min_num), sizeof(int)); // 将最小的数写入输出文件中 files[min_pos].read(reinterpret_cast<char*>(&nums[min_pos]), sizeof(int)); // 从文件中读入一个数 if (files[min_pos].peek() == EOF) { // 如果该文件已经读完,则关闭文件 files[min_pos].close(); } } } int main() { const char* input_file = "input.bin"; const char* output_file = "output.bin"; read_file(input_file); // 读入输入文件,并将其中的数据插入到散列表中 merge_files(output_file); // 合并多个文件中的数据,并将结果写入到输出文件中 return 0; } ``` 上述代码实现了将输入文件中的数据插入到散列表中,并将散列表中的数据排序后写入到缓冲区中,当缓冲区满了之后再将缓冲区中的数据写入到硬盘中。当处理完输入文件中的所有数据后,将散列表中的数据写入到缓冲区中,并将缓冲区中的数据写入到硬盘中。最后,将多个文件中的数据合并,得到最终的排序结果。在将散列表中的数据写入到硬盘中时,可以采用多线程并发的方式对每个桶进行排序,提高排序的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值