算法通关村第十五关——用4kb内存寻找重复元素(青铜)

算法通关村第十五关——用4kb内存寻找重复元素(青铜)

1 超大规模数据场景

1.1 概念

超大规模数据场景是指处理海量数据的情况,通常涉及到大规模数据存储、快速数据处理和高效的数据分析。这种场景下,数据的规模非常庞大,可能达到或超过PB级别(1 PB = 1024 TB),需要使用分布式计算和存储系统来处理和存储这些数据。

在超大规模数据场景中,常见的应用包括互联网搜索引擎、社交网络数据分析、金融风控系统、物流路线规划等。在这些应用中,数据的产生速度非常快,同时需要进行复杂的数据分析和挖掘,因此需要高性能的硬件设备和优化的算法来处理数据。

1.3 解决方法

  1. 分治法:将大问题分解成小问题进行处理,然后再将结果合并。例如,可以将一个大文件分割成多个小文件进行处理,最后将结果汇总。
  2. 并行计算:利用多核或者分布式计算的优势,将任务分发给多个计算单元进行并行计算,加快处理速度。
  3. 压缩技术:对于大规模数据,可以使用压缩技术来减小数据的存储空间,从而提高处理效率。
  4. 数据预处理:对于大规模数据集,可以通过一些预处理操作,如排序、索引等,提前优化数据结构,减少后续计算的复杂度。
  5. 分布式存储与计算:将数据分布存储在多台机器上,并利用分布式计算框架进行计算,可以有效地处理大规模数据。

1.2 解决算法

在大部分算法中,默认给定的数据量都很小的,例如只有几个或者十几个元素,但是如果将数据量提高到百万甚至十几亿,那处理逻辑就会发生很大差异,这也是算法考查中,经常出现的一类问题。此时普通的数组、链表、Hash、树等等结构有无效了 ,因为内存空间放不下了。而常规的递归、排序,回溯、贪心和动态规划等思想也无效了,因为执行都会超时,必须另外想办法。这类问题该如何下手呢?这里介绍三种非常典型的思路:

  1. 使用位存储,使用位存储最大的好处是占用的空间是简单存整数的1/8。例如一个40亿的整数数组,如果用整数存储需要16GB左右的空间,而如果使用位存储,就可以用2GB的空间,这样很多问题就能够解决了。

  2. 如果文件实在太大 ,无法在内存中放下,则需要考虑将大文件分成若干小块,先处理每个块,最后再逐步得到想要的结果,这种方式也叫做外部排序。这样需要遍历全部序列至少两次,是典型的用时间换空间的方法。

  3. 堆,如果在超大数据中找第K大、第K小,K个最大、K个最小,则特别适合使用堆来做。而且将超大数据换成流数据也可以,而且几乎是唯一的方式,口诀就是“查小用大堆,查大用小堆”。

2 题目1:寻找重复数(4kb)

2.1 无4kb要求

leetcode 287. 寻找重复数

方法有很多种,这里就不仔细展开讲了

2.2 有4kb要求

题目要求:给定一个数组,包含从1到N的整数,N最大为32000,数组可能还有重复值,且N的取值不定,若只有4KB的内存可用,该如何打印数组中所有重复元素。

分析:本身是一道海量数据问题的热身题,如果去掉“只有4KB”的要求,我们可以先创建一个大小为N的数组,然后将这些数据放进来,但是这里数组最大为32KB,而题目有4KB的内存限制,我们就必须先确定该如何存放这个数组。

如果只有4KB的空间,那么只能寻址8 X 4 X 2^10个比特,这个值比32000要大的.

补充:

在计算机中,存储容量常以字节(byte)为单位。一个字节由8个比特(bit)组成。因此,如果只有4KB的空间,那么存储容量将是4 * 1024字节。由于每个字节有一个唯一的地址,并且一个字节由8个比特组成。

4 * 1024字节 * 8比特/字节 = 32768比特

因此我们可以创建32000比特的位向量(比特数组),其中一个比特位置就代表一个整数。利用这个位向量,就可以遍历访问整个数组。如果发现数组元素是v,那么就将位置为v的

设置为1,碰到重复元素,就输出一下。

public class FindDuplicatesIn32000 {
    
    // 检查重复元素的方法
    public void checkDuplicates(int[] array) {
        // 创建一个大小为32000的位图(BitSet)对象
        BitSet bs = new BitSet(32000);
        
        // 遍历输入数组
        for (int i = 0; i < array.length; i++) {
            int num = array[i];
            int num0 = num - 1;
            
            // 检查位图中索引为num0的位是否已经被设置为1
            if (bs.get(num0)) {
                System.out.println(num); // 如果是重复元素,则打印出来
            } else {
                bs.set(num0); // 如果不是重复元素,则将位图中索引为num0的位设置为1
            }
        }
    }
    
    // 自定义位图数据结构
    class BitSet {
        int[] bitset;
        
        // 构造函数,初始化位图
        public BitSet(int size) {
            this.bitset = new int[size >> 5]; // 每个int可以表示32个比特,所以除以32
        }
        
        // 获取指定位置的比特位
        boolean get(int pos) {
            int wordNumber = (pos >> 5); // 除以32,计算在数组中的索引
            int bitNumber = (pos & 0x1F); // 取余32,计算在当前int中的位置
            return (bitset[wordNumber] & (1 << bitNumber)) != 0; // 检查指定位置的比特位是否为1
        }
        
        // 设置指定位置的比特位为1
        void set(int pos) {
            int wordNumber = (pos >> 5); // 除以32,计算在数组中的索引
            int bitNumber = (pos & 0x1F); // 取余32,计算在当前int中的位置
            bitset[wordNumber] |= 1 << bitNumber; // 将指定位置的比特位设置为1
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值