从数组到HashMap之算法解释

一 数组是什么?

  忘了在哪本书里曾看到过类似这样的一句话“所有的数据结构都是数组的演化”,想想其实是有道理的,因为计算机的内存其实就是线性的存储空间。
  Java示例代码:int[] array = new int[5]
  忽略对象头信息和数组长度信息,JVM执行时会在堆中分配20个字节的内存空间,看起来就是这样的:

  

  这样的数据结构可以很方便地通过数组下标存取数据,但在查找时需要遍历数组,平均时间复杂度为O(n/2)。

  当数据量很大或者查找操作频繁的时候,这样的遍历操作几乎是不可接受的。那么,如何才能够在更短的时间内快速找到数据呢?

 

二 二分查找

  假如数组内的元素是已经排序的,那么很自然的方式就是使用二分查找。
  譬如有一个整形数组的长度为1000,数组内的整数从小到大排列,如果我想要知道6000是否在此数组中。那么我可以先查看array[500]的数字是否为6000,array[500]的数字比6000小,那么就查看array[750]位置的数字……依次类推,那么最多经过10次,就可以确定结果。
  此算法的时间复杂度为O(logN)。

  然而,大部分情况下数组元素都是无序的,而排序所需要的时间复杂度O(NlogN)通常都远远超过遍历所需要的时间。

  那么,问题又回到了原点。该如何在无序的数据中快速找到数据呢?

 

三 懵懂的思考

  还记得刚学编程不久时看《编程珠玑》,其中有一段描述:20世纪70年代,Mike Lesk为电话公司做了电话号码查找功能,譬如输入LESK*M*对应的数字键5375*6*,可以返回正确的电话号码或可选列表,错误匹配率仅0.2%。
  怎么才能做到?

  当时我还完全不了解数据结构和算法,还原下当时天马行空思考的过程,现在看起来仍然是很有意思的。

 

  ㈠ 加法

  所有数字相加(*键为11),5375*6*的和为48。大多数输入的和不超过100,意味着几万个电话号码都会聚集在数组前100的位置,所以是不可行的。

 

  ㈡ 乘法

  所有数字相乘,积为381150。这似乎是可行的,但出现了这样的问题:lesk、lsek、slke……的积是一样的,每一个数字键还对应着3个字母,意味着重复的概率会很高。

 

  ㈢ 改进后的乘法

  ①字母相同但字母序不同的字符串,可以通过这样的方式来避免冲突:每一个数字先乘以序号,然后再与其它值相乘。
  
  ②每一个数字键对应了3个英文字母,如果用户没有输入字母在数字键中的序号,是没办法再进一步精确计算的。能考虑的方向只能是根据给定的单词表来做概率统计,所以不予考虑。

 

  ㈣ 位置冲突

  即使用改进后的乘法,不同的姓名字母计算得出的积依然还是可能会发生重复,那么当发生冲突后应该怎么办?
  顺序保存到下一个空白位置?仔细想想这是行不通的。如果下一个空白位置刚好又是另外的字母集合的积,那就产生了二次冲突。

  幸好,还有质数。
  质数只能被1和自身整除,那么上述乘法得出的任何积都不可能是质数,所以质数位置是没有保存电话号码的。
  因此,以当前积为起点向右搜索下一个质数,如果该质数位置依然被使用,那么就继续查找下一个最近的质数……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值