当前位置: 首页 > news >正文

[Go版]算法通关村第十五关青铜——用4KB内存寻找重复元素

目录

海量数据中,此时普通的数组、链表、Hash、树等等结构有无效了 ,因为内存空间放不下了。而常规的递归、排序,回溯、贪心和动态规划等思想也无效了,因为执行都会超时,必须另外想办法。这类问题该如何下手呢?这里介绍三种非常典型的思路:

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

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

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

题目:用4KB内存寻找重复元素

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

思路分析:使用位存储

如何存储这32000个整数?

常规思路分析:32000个整数,整数用int表示,一个int占用4个字节(byte),32000个整数所需内存就是 :

32000 * 4 = 128000(byte)
32000 * 4 / 1024 = 125(KB)
125(KB) > 4(KB)	//可见,已经超过题目要求的4KB内存要求。

下面我们使用位存储的方式:1个字节(byte)=8位(bit),32000个正数用32000个位就是:

32000 / 8 = 4000(byte)
32000 / 8 / 1024 = 3.9(KB)
3.9(KB)< 4(KB)	//如此,就满足题意,使用了4KB就能存储32000个元素

每个整数对应在位图中的存储状态举例

原数据:				1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18  ...
该值在位图中的索引值:	0  0  0  0  0  0  0  1  1  1  1  1  1  1  1  2  2  2  ... 
该值在位图中的偏移量:	1  2  3  4  5  6  7  0  1  2  3  4  5  6  7  0  1  2  ...1 对应的位图值,和二进制值为:byteMap[0]	00000010
2 对应的位图值,和二进制值为:byteMap[0]	00000100
3 对应的位图值,和二进制值为:byteMap[0]	00001000
4 对应的位图值,和二进制值为:byteMap[0]	00010000
5 对应的位图值,和二进制值为:byteMap[0]	00100000
6 对应的位图值,和二进制值为:byteMap[0]	01000000
7 对应的位图值,和二进制值为:byteMap[0]	10000000
8 对应的位图值,和二进制值为:byteMap[1]	00000001
9 对应的位图值,和二进制值为:byteMap[1]	00000010
...

如何判断是重复的?

既然我们用一个位(bit)代表一个数值,那么该位的两种状态0或1,就可以用于判断该值是否存在。
例如:字节00001101表示以下情况:

  • 第 0 位(最低位)为 1,表示数字 1 出现过。
  • 第 1 位为 0,表示数字 2 没有出现过。
  • 第 2 位为 1,表示数字 3 出现过。
  • 第 3 位为 1,表示数字 4 出现过。
  • 后续位为 0,表示数字 5 到 8 都没有出现过。
mark := 1 << offset	//offset 就是偏移量
if (bitmap[index] & mask) != 0 {// 位已经被设置,说明数字出现过
}
bitmap[index] |= mask	//设置该位值为1

具体的步骤

位图(Bitmap)是一种数据结构,用于表示一组元素的状态或属性,通常用二进制位来表示,每个位代表一种状态或属性。在计算机科学中,位图被广泛用于各种应用,如图像处理、数据压缩、数据库索引等。

  1. 初始化位图:由于N最大是32000,可以是哦用一个长度为32000/8=4000的位图,每个位可以表示一个整数。
  2. 遍历数组,对于数组中的每个元素:
    • 计算x在位图中的索引和位偏移。例如:x=5,则索引为5/8=0,位偏移为5%8=5。
    • 检查位图的索引位置是否已经被标记。
      • 如果未被标记,则将其标记为已访问;
      • 如果已经被标记,则说明x是重复的,打印x。
  3. 打印重复元素。

复杂度:时间复杂度 O ( n ) O(n) O(n)、空间复杂度 O ( 1 ) O(1) O(1)

Go代码

源码地址: GitHub-golang版本(含单元测试代码)

func FindDuplicatesIn32000(arr []int) (duplicate []int) {N := 32000bitmap := make([]byte, N/8+1)for _, num := range arr {// 计算 num 在 bitmap 中的索引// index := num / 8index := num >> 3// 计算 num 在 bitmap 中的偏移量offset := num % 8mark := byte(1 << offset)if bitmap[index]&mark != 0 {duplicate = append(duplicate, num)} else {bitmap[index] |= mark}}return
}

或者

func FindDuplicatesIn32000(arr []int) (duplicate []int) {N := 32000// 或者这里不用+1,只要索引是base0的即可bitmap := make([]int, N/32)for _, num := range arr {num0 := num - 1 //base0开始index := num0 / 32offset := num0 % 32mark := 1 << offsetif bitmap[index]&mark != 0 {duplicate = append(duplicate, num)} else {bitmap[index] |= mark}}return
}
http://www.lryc.cn/news/152221.html

相关文章:

  • OJ练习第159题——消灭怪物的最大数量
  • Prometheus-Rules(规则)
  • 打卡智能中国(六):村里出了“飞行员”
  • 自动化运维工具Ansible之playbooks剧本
  • K8S访问控制------认证(authentication )、授权(authorization )、准入控制(admission control )体系
  • 开开心心带你学习MySQL数据库之第三篇上
  • Mysql批量插入大量数据的方法
  • centos安装nginx实操记录(加安全配置)
  • 【中等】49. 字母异位词分组
  • Python3 条件控制
  • IDEA自定义模板
  • 【Unity3D】UI Toolkit简介
  • QT 界面相关操作
  • nestjs:docker build时执行npm install sharp提示downloading libvips socket hang up
  • 图像分类学习笔记(七)——MobileNet
  • ssm+vue宠物领养系统源码和论文
  • 阜时科技联合客户发布全固态激光雷达面阵SPAD芯片及雷达整机
  • leetcode 189. 轮转数组
  • 亚马逊广告收入突破百亿美元,有望成为下一个收入支柱来源?
  • MATLAB中isequal函数转化为C语言
  • 【MTK平台】根据kernel log分析wifi scan的时候流程
  • CVE-2023-23752:Joomla未授权访问漏洞复现
  • MATLAB中circshift函数转化为C语言
  • 浅谈React生命周期
  • 基于龙格-库塔算法优化的BP神经网络(预测应用) - 附代码
  • C++ 获取进程信息
  • 【Redis从头学-13】Redis哨兵模式解析以及搭建指南
  • 【个人笔记js的原型理解】
  • Liunx系统编程:信号量
  • 大集合按照指定长度进行分割成多个小集合,用于批量多次处理数据