• 04 查找与哈希算法


    静态查找(Static Search):在查找过程中,该查找数据不会有添加、删除或更新等操作。
    动态查找 (Dynamic Search)

    • 顺序查找法/线性查找法
      将数据一项一项地按顺序逐个查找,不管数据顺序如何,都得从头到尾遍历一次。
      优点:查找前不需要任何处理与排序。
      缺点:慢。适合小数据。

    • 二分法查找
      数据事先 排好序 了。
      将数据分割成两等份,再比较键值与中间值的大小,如果键值小于中间值,可确定要查找的数据在前半段,否则在后半段。

        def bin_search(data, val):
            low = 0
            high = len(data) - 1
            while low <= high:
                mid = (low+high) // 2
                if data[mid] < val:
                    low = mid + 1
                elif data[mid] > val:
                    high = mid - 1
                else:
                    return mid
            return -1
      
    • 插值查找法/插补查找法(Interpolation Search)
      二分法的改进。
      按照数据位置的分布,利用公式预测数据所在位置,在以二分法的方式逐渐逼近。
      使用插值法是假设数据平均分布在数组中,而每一项的数据的差距相当接近或有一定的距离比例。
      插值法的公式为:
      Mid = low + (( key - data[low] ) / ( data[high] - data[low] )) * ( high - low )
      其中,key 是要查找的键,data[high],data[low]是剩余待查数据中的最大、最小值。
      假设数据有 n 项,插值法查找的步骤如下:
      ① 将记录按从小到大的顺序给予1、2、3、...、n的编号
      ② 令 low = 1,high = n
      ③ 当 low < high 时,重复 ④ 和 ⑤
      ④ 令 Mid = low + (( key - data[low] ) / ( data[high] - data[low] )) * ( high - low )
      ⑤ 若 key < keyMid 且 high ≠ Mid - 1,则令 high = Mid - 1;若 key = keyMid 成功查到;若 key > keyMid 且 low ≠ Mid + 1,则令 low = Mid + 1

        def interpolation_search(data, val):
            low = 0
            high = len(data) - 1
            while low < high:
                mid = low + int((val - data[low]) * (high - low) / (data[high] - data[low]))
                if mid > high:
                    return -1
                if data[mid] > val:
                    high = mid - 1
                elif data[mid] < val:
                    low = mid + 1
                else:
                    return mid
            return -1
      
    • 常见哈希法

      • 除留余数法
        将数据除以某一个常数后,取余数来当索引。
        h( key ) = key mod B
        B 最好是质数
      • 平方取中法
        例如 65,求平方得 4225,取百位数和十位数作为键值。f( 22 ) = 65。
        如果有10个空间,而取百位和个位数的有100个(00~99)。需要把第一次求得的键除以 10 取整(压缩),才能将 100 个可能产生的值对应到 10 个空间。f( 2 ) = 65。
      • 折叠法
        将数据转换成一串数字后,先将这串数字拆成几个部分,再把它加起来,就可以计算出这个键值的 Bucket Address(桶地址)。
        例如,转换后的数字位 1238765439873,以 4 个数字为一部分,1238、7654、3987、3,将四个数字相加即为索引值。这种直接相加的做法称为”移动折叠法“。
        为了降低碰撞,可以将每一部分数字中的奇数和偶数反转再相加。这种做法叫”边界折叠法“(folding at the boundaries)。例如将偶数反转,8321、4567、3987、3 相加得出桶地址。
      • 数字分析法
        适用于数据不会更改,且为数字类型的静态表。
        在决定哈希函数时先逐一检查数据的相对位置和分布情况,将重复性高的部分删除。
    • 溢出( Overflow )与碰撞( Collision )

      • 线性探测法
        当发生碰撞时,若该索引对应的存储位置已有数据,则以线性的方式往后往后寻找空的存储位置,一旦找到就把数据放进去。线性探测法通常把哈希的位置视为环形结构,后面的位置被填满前面还有位置时,可以将数据放到前面。
          import random
        
          INDEXBOX = 10  # 哈希表可放元素个数
          MAXNUM = 7  # 数据个数
        
          def print_data(data, max_number):  # 打印数组子程序
              print('	', end='')
              for i in range(max_number):
                  print('[%2d] ' % data[i], end='')
              print()
        
          def create_table(num, index):  # 建立哈希表子程序
              tmp = num % INDEXBOX  # 哈希函数 = 数据 % INDEXBOX
              while True:
                  if index[tmp] == -1:  # 如果数据对应的数据是空的
                      index[tmp] = num  # 则直接存入数据
                      break
                  else:
                      tmp = (tmp + 1) % INDEXBOX  # 否则往后找位置存放
         
         # 主程序
          index = [None] * INDEXBOX
          data = [None] * MAXNUM
          print('原始数组值:')
          for i in range(MAXNUM):
              data[i] = random.randint(1, 20)
          for i in range(INDEXBOX):
              index[i] = -1
          print_data(data, MAXNUM)
        
          print('哈希表内容:')
          for i in range(MAXNUM):
              create_table(data[i], index)
              print(' %2d =>' %data[i], end='')
              print_data(index, INDEXBOX)
        
          print('完成哈希表:')
          print_data(index, INDEXBOX)
        

        原始数组值:
        [ 7] [13] [13] [12] [14] [15] [12]
        哈希表内容:
        7 => [-1] [-1] [-1] [-1] [-1] [-1] [-1] [ 7] [-1] [-1]
        13 => [-1] [-1] [-1] [13] [-1] [-1] [-1] [ 7] [-1] [-1]
        13 => [-1] [-1] [-1] [13] [13] [-1] [-1] [ 7] [-1] [-1]
        12 => [-1] [-1] [12] [13] [13] [-1] [-1] [ 7] [-1] [-1]
        14 => [-1] [-1] [12] [13] [13] [14] [-1] [ 7] [-1] [-1]
        15 => [-1] [-1] [12] [13] [13] [14] [15] [ 7] [-1] [-1]
        12 => [-1] [-1] [12] [13] [13] [14] [15] [ 7] [12] [-1]
        完成哈希表:
        [-1] [-1] [12] [13] [13] [14] [15] [ 7] [12] [-1]

      • 平方探测法
        当溢出发生时,下一次查找的地址是 ( f(x)+i² ) mod B 与 ( f(x) - i² ) mod B。例如数据值 key,哈希函数 f:
        第一次查找:f ( key )
        第二次查找:( f ( key ) + 1² ) % B
        第三次查找:( f ( key ) - 1² ) % B
        第四次查找:( f ( key ) + 2² ) % B
        第五次查找:( f ( key ) - 2² ) % B
        ... ...
        第 n 次查找:( f ( key ) ± ( ( B-1 ) / 2 )² ) % B,其中,B必须为 4j + 3 型的质数,且 1 ≤ i ≤ ( B-1 ) / 2
      • 再哈希法
        先设置一系列哈希函数,如果使用第一种哈希函数出现溢出时就改用第二种,如果第二种也溢出,就是用第三种,...,一直到没有溢出。

    用哈希法将下列 7 个数字存在0、1...6的 7 个位置:101,186,16,315,202,572,463。若要存入 1000 开始的 11 个 位置,如何存放?
    data mod 7
    ( data mod 11 ) + 1000

    碰撞:两个不同的数据经过哈希运算对应到相同的地址。

  • 相关阅读:
    npm包发布过程
    react树状组件
    js数据结构处理--------扁平化数组处理为树结构数据
    js数据结构处理--------树结构数据遍历
    JS fetch
    JS promise
    JS 闭包
    JS 异步回调
    三角形加正方形
    webAPI的分类
  • 原文地址:https://www.cnblogs.com/catyuang/p/11582786.html
Copyright © 2020-2023  润新知