第十章 映射、哈希表和跳跃表的基本知识及python实现(from Goodrich)

Date: 2019-7-11

目录:

10.1 映射和字典(10.1.1 映射的抽象数据类型 10.1.2 应用:单词频率统计  10.1.3 python的MutableMapping抽象基类 10.1.4 我们的MapBase基类 10.1.5 简单的非有序映射实现)

10.2 哈希表(10.2.1 哈希函数 10.2.2 哈希码  10.2.3 压缩函数  10.2.4 冲突处理方案 10.2.5 负载因子、重新哈希和效率 10.2.6 python哈希表的实现)

10.3 有序映射(10.3.1 排序检索表 10.3.2 有序映射的两种应用)

10.4 跳跃表(10.4.1 跳跃表中的查找和更新操作 10.4.2 跳跃表的概率分析(略) )

10.5 集合、多集、多映射(10.5.1 集合的抽象数据类型 10.5.2 python的MutableSet抽象基类 10.5.3 集合、多集和多映射的实现)

10.1 映射和字典

dict类可以说是python语言中最重要的数据结构。它表示一种称作字典的抽象,在其中每个唯一的关键字都被映射到对应的值上。由于字典所表示的键和值之间的关系,我们通常称其为关联数组(associative array)或者映射(map)。在此,我们使用术语字典(dictionary)来讨论python的dict类,并且使用术语映射(map)来讨论抽象数据类型的更一般的概念。

映射的索引不需要连续和数字化(不同于数组),例如:

1.大学信息系统依赖于某种形式的映射,这种映射以学生ID作为键,并且将其映射到学生相关的记录

2.域名系统(DNS)将主机名映射到一个网络协议(IP)地址,这样的键可以高效地映射到特定用户的相关信息上。

3.计算机图像系统上可以将一个颜色名称映射到用于描述颜色RGB(红-绿-蓝)的三元组上。

10.1.1 映射的抽象数据类型

下面是映射M的最为重要的五类行为:

M[k]如果存在,返回映射M中与键k相对应的值,否则返回KeyError错误。在Python中是由__getitem__实现的
M[k]=v将映射M中的键k与值v建立关联,如果映射中的键k已经有了对应的值存在,则进行替换该值,由__setitem__实现
del M[k]从映射M中删除键为k的元组,如果M中不存在这样的元组,则返回KeyError错误,有__delitem__实现
len(M)返回映射M中元组的数量,由__len__实现。
iter(m)默认的对一个映射迭代生成其中包含所有键的序列,由__iter__实现,并且支持以for k in M形式控制的循环

映射M还应该支持以下操作:

k in M如果映射中包含键为k的元组则返回True,有__contains__实现
M.get(k, d=None)如果映射中存在键k则返回M[k],否则返回缺省值d。这种方法提供了一种避免返回KeyError风险的M[k]查询方法。
M.setdefault(k,d)如果映射中存在键k则返回M[k],而如果键k不存在,则设置M[k]=d,并返回这个值。
M.pop(k,d=None)从映射M中删除键为k的元组,并返回与其对应的值v。如果不存在则返回缺省值None
M.popitem()从映射M中随机删除一个key-value键值对,并返回一个用于表示被删除的键值对的(k,v)数据元组。如果为空,则KeyyError.
M.clear()从映射中删除所有的key-value键值对
M.keys()返回一个含有映射M中所有键的集合的视图
M.values()返回一个含有映射M中所有值的集合的视图
M.items()返回一个含有M中所有键值对元组的集合
M.update(M2)对于M2中每一个(k,v)对进行赋值,设置M[k]=v
M == M2如果映射M和映射M2中所包含的key-value键值对完全相同,则返回True
M != M2如果映射M和M2包含有不同的key-value键值对,则返回True.

10.1.2 应用:单词频率统计

这种应用是将单词作为键,单词出现的次数作为相应的值,构成键值对。

下面是:一个统计单词出现频率并报告出现频率最高的单词的程序。

import sys
filename = sys.argv[1]
# 构建字典dict
freq = {}
for piece in open(filename).read().lower().split():
  # only consider alphabetic characters within this piece
  word = ''.join(c for c in piece if c.isalpha())
  if word:                                # require at least one alphabetic character
    freq[word] = 1 + freq.get(word, 0)
# 寻找max frequency word
max_word = ''
max_count = 0
for (w,c) in freq.items():    # (key, value) tuples represent (word, count)
  if c > max_count:
    max_word = w
    max_count = c
print('The most frequent word is', max_word)
print('Its number of occurrences is', max_count)

10.1.3 python的MutableMapping抽象基类

在抽象基类中,被定义为抽象的方法必须由具体的子类实现。然而,一个抽象的基类可以提供其他方法的具体实现,这取决于所使用的假定的抽象方法。collections模块提供了两个与我们现在所讨论的内容相关的抽象基类:Mapping和MutableMapping.Mapping类包含由python的dict类所支持的所有不变的方法,而MutableMapping类扩展包含所有 可变的方法。这些抽象基类的意义在于他们提供了一个框架以帮助创建用户自定义的Map类。但不提供__getitem__、__setitem__、__delitem__、__len__、__iter__这五个行为,这些行为是在具体的子类中根据情况而设定的。

10.1.4 我们的MapBase类

# 10-2 通过扩展MutableMapping抽象基类实现非公有类_Item,以满足各种映射应用。
from collections import MutableMapping

class MapBase(MutableMapping):
  """Our own abstract base class that includes a nonpublic _Item class."""

  #------------------------------- nested _Item class -------------------------------
  class _Item:
    """Lightweight composite to store key-value pairs as map items."""
    __slots__ = '_key', '_value'

    def __init__(self, k, v):
      self._key = k
      self._value = v

    def __eq__(self, other):               
      return self._key == other._key   # compare items based on their keys

    def __ne__(self, other):
      return not (self == other)       # opposite of __eq__

    def __lt__(self, other):               
      return self._key < other._key    # compare items based on their keys

10.1.5 简单的非有序映射的实现

通过一个简单的Map ADT的具体实现来说明MapBase类的使用。下面是一个unsortedTableMap类,它依赖于在python列表中以任意顺序存储键值对。

# 10-3 一个用Python列表作为非排序列表的map实现方法。
from .map_base import MapBase

class UnsortedTableMap(MapBase):
  """Map implementation using an unordered list."""

  def __init__(self):
    """Create an empty map."""
    self._table = []                              # list of _Item's
  
  def __getitem__(self, k):      # 1 索引键为key的值
    """Return value associated with key k (raise KeyError if not found)."""
    for item in self._table:
      if k == item._key:
        return item._value
    raise KeyError('Key Error: ' + repr(k))

  def __setitem__(self, k, v):    # 2 修改(k,v)的键值对
    """Assign value v to key k, overwriting existing value if present."""
    for item in self._table:
      if k == item._key:                          # Found a match:
        item._value = v                           # reassign value
        return                                    # and quit    
    # did not find match for key
    self._table.append(self._Item(k,v))

  def __delitem__(self, k):       # 3 删除键为k的值
    """Remove item associated with key k (raise KeyError if not found)."""
    for j in range(len(self._table)):
      if k == self._table[j]._key:                # Found a match:
        self._table.pop(j)                        # remove item
        return                                    # and quit    
    raise KeyError('Key Error: ' + repr(k))
  
  def __len__(self):              # 4 计算长度
    """Return number of items in the map."""
    return len(self._table)

  def __iter__(self):             # 5 生成迭代器        
    """Generate iteration of the map's keys."""
    for item in self._table:
      yield item._key                             # yield the KEY

在这个map构造器中,将一个空的表格初始化为self._table.当一个新的键被放入map中,通过__setitem__函数,我们创建了一个嵌套类_Item的实例,该嵌套类继承自MapBase类。但给非有序映射的效率不高,因为他们都依赖一个for循环扫描列表中的元组,以搜素匹配的键。

 

10.2 哈希表

哈希表应该是一个最实用的实现map/dict类的数据结构。哈希表的一个新概念是使用哈希函数将每个一般的键映射到一个表中的相应索引上。在理想情况下,键将由哈希函数分布到从0到N-1的范围内,但是在实践中可能有两个或者更多的不同的键被映射到同一个索引上(冲突)。

10.2.1 哈希函数

哈希函数h的目标就是把每个键k映射到[0,N-1]区间内的整数,其中N是哈希表的桶数组的容量。使用这种哈希函数h的主要思想是使用哈希函数值h(k)作为哈希函数桶数组A内部的索引,而不用键k做索引,也就是说在桶A[h(k)]中存储元组(k,v).

如果有两个或者更多的键具有相同的哈希值,那么两个不同的元组将被映射到相同的桶A中。这种情况被称为‘冲突’.

评价哈希函数h(k)常见的方法由两部分组成:一个哈希码,将一个键映射到一个整数;另一个是压缩函数,将哈希码映射到一个桶数组的索引,这个索引是范围在区间[0,N-1]的一个整数。

将哈希函数分成两部分的优点:

哈希码计算部分独立于具体的哈希表的大小。这样就可以为每个对象开发一个通用的哈希码,并且可以用于任何大小的哈希表,只有压缩函数与表的大小有关。这样就特别方便,因为哈希表底层的桶数组可以根据当前存储在映射中的元组数动态调整大小。

10.2.2 哈希码

哈希函数执行的第一步是取出映射中的任意一个键k,并且计算得到一个整数作为键k的哈希码;这个整数不需要在[0,N-1]f范围内,甚至可以是负数。我们希望分配给键的哈希码集合尽可能避免冲突。因为如果哈希码都产生了冲突,经过压缩函数之后必然是冲突的。python哈希码可以通过以下方式实现:

* 将位作为整数处理

32位键的处理,64位键的处理:高阶32位和低阶32位采用一定的方式进行合并,生成一个32位的哈希码。

* 多项式哈希码

字符串或其他用(x_0,x_1,x_2,\cdots,x_{n-1})元组形式表示的,可以采取这样的多项式将其转换成哈希码:x_0a^{n-1}+x_1a^{n-2}+\cdots+x_{n-2}a+x_{n-1}.直观而言,一个多项式的哈希码通过乘以不同权值方式来分散每一部分对哈希码结果的影响。实验表明,a一般取33、37、39、41效果更好。

* 循环移位哈希码

一个多项式哈希码的变种,是用一定数量的位循环位移得到部分和来替代乘以a.

* python中的哈希码

在pythonh中计算哈希码的标准机制是一个内置签名hash(x)函数,该函数将返回一个整数型值作为对象x的哈希码。然而在python中,只有不变的数据类型是可哈希的。在默认情况下,用户定义的实例被视为不可哈希的,并且哈希函数会产生TypeError。

10.2.3 压缩函数

通常,键k的哈希码不适合立即用于桶数组,因为整数哈希码可能是负或可能超过桶数组的容量。一个很好的压缩函数或使给定一组哈希码的冲突数达到最小。

* 划分方法

一个简单的压缩函数是这样划分的,他将一个整数i映射到N:i mod N

* MAD方法

有一个更为复杂的压缩函数可以帮助一组整数键消除重复模式,即Multiply-Add-and-Divid(MAD)方法,是通过:

[(ai+b) mod p] mod N.对i进行映射,这里N是桶数组的大小,p是比N大的素数,a和b是从区间[0,p-1]任意选择的整数,并且a>0.选择这个压缩函数是为了消除在哈希码集合中的重复模式,并且得到更好的哈希函数,因为该函数使得任意两个键冲突的概率是1/N.

10.2.4 冲突处理方案

虽然上面的方法能够在一定程度上减少冲突,但是冲突仍然是有可能发生,不可避免的。下面将介绍几种处理冲突的方法:
* 分离链表

处理冲突的一个简单且有效的方式是使每个桶A[j]存储其自身的二级容器,容器存储元组(k,v),如果h(k)=j。用一个很小的list容器来实现map实例是实现二级容器很自然的选择。

* 开放寻址

分离链表遇到复杂的桶内数组时,时间效率和空间大小较为复杂。开放寻址需要负载因子总是最大不超过1,并且元组直接存储在桶数组自身的单元中。

  **线性探测及其变种

     使用开放寻址处理冲突的一个简单的方法是线性探测。使用这种方法时,如果我们想要将一个元祖(k,v)插入桶A[j]处,在这里j=h(k),但A[j]已经被占用,那么我们将尝试插入A[(j+1) mod N];如果A[(j+1) mod N]也已经被占用了,则我们将尝试A[(j+2) mod N],如此重复操作,直到找到一个可以接受新元组的空桶。

  ** 二次探测

     该方法将反复探测桶A[h(k)+f(i) mod N],i=0,1,2,……,其中f(i)=i^2;线性探测和二次探测都会产生一个聚集问题。

*** 双哈希策略

    一种不会引起如线性探测和二次探测所产生的聚集问题的开放寻址策略称为双哈希策略。

10.2.5 负载因子、重新哈希和效率

10.2.6 python 哈希表的实现

在该部分,我们介绍两种哈希表的实现,一种是分离链表,另一种是使用包含线性探测的开放寻址。HashMapBase类主要的设计元素是:

1) 桶数组由一个python列表表示,名为self._table,并且所有的条目初始为None

2) 维护一个self._n实例变量用来表示当前存储在哈希表中不同数组的个数

3)如果表格的负载因子增加到超过0.5,我们会将哈希表的大小扩大2倍并且将所有元素重新哈希到新表中

4)我们定义一个_hash_函数的方法,该方法依靠python内置哈希函数来生成键的哈希码,并用MAD公式生成压缩函数

# 一个哈希表实现的基类。
from .map_base import MapBase
from collections import MutableMapping
from random import randrange         # used to pick MAD parameters

class HashMapBase(MapBase):
  """Abstract base class for map using hash-table with MAD compression.

  Keys must be hashable and non-None.
  """

  def __init__(self, cap=11, p=109345121):
    """Create an empty hash-table map.

    cap     initial table size (default 11)
    p       positive prime used for MAD (default 109345121)
    """
    self._table = cap * [ None ]
    self._n = 0                                   # number of entries in the map
    self._prime = p                               # prime for MAD compression
    self._scale = 1 + randrange(p-1)              # scale from 1 to p-1 for MAD
    self._shift = randrange(p)                    # shift from 0 to p-1 for MAD

  def _hash_function(self, k):
    return (hash(k)*self._scale + self._shift) % self._prime % len(self._table)

  def __len__(self):
    return self._n

  def __getitem__(self, k):
    j = self._hash_function(k)
    return self._bucket_getitem(j, k)             # may raise KeyError  # 后续用不同的实现具体化

  def __setitem__(self, k, v):
    j = self._hash_function(k)
    self._bucket_setitem(j, k, v)                 # subroutine maintains self._n  # 后续用不同的实现具体化
    if self._n > len(self._table) // 2:           # keep load factor <= 0.5
      self._resize(2 * len(self._table) - 1)      # number 2^x - 1 is often prime

  def __delitem__(self, k):
    j = self._hash_function(k)
    self._bucket_delitem(j, k)                    # may raise KeyError  # 后续用不同的实现具体化
    self._n -= 1

  def _resize(self, c):
    """Resize bucket array to capacity c and rehash all items."""
    old = list(self.items())       # use iteration to record existing items
    self._table = c * [None]       # then reset table to desired capacity
    self._n = 0                    # n recomputed during subsequent adds
    for (k,v) in old:
      self[k] = v                  # reinsert old key-value pair

基类中定义了以下的抽象方法,其在具体的子类中实现:

_bucket_getitem(j,k)这个方法在桶j中搜索查找键为k的元组
_bucket_setitem(j,kv)这个方法将桶j中键为k的值修改为v
_bucket_delitem_(j,k)这个方法删除桶j中键为k的元组
__iter__这是遍历map所有键的标准map方法

分离链表的具体实现:

# y用分离链表实现的具体哈希map类
from .hash_map_base import HashMapBase
from .unsorted_table_map import UnsortedTableMap

class ChainHashMap(HashMapBase):
  """Hash map implemented with separate chaining for collision resolution."""

  def _bucket_getitem(self, j, k):
    bucket = self._table[j]
    if bucket is None:
      raise KeyError('Key Error: ' + repr(k))        # no match found
    return bucket[k]                                 # may raise KeyError

  def _bucket_setitem(self, j, k, v):
    if self._table[j] is None:
      self._table[j] = UnsortedTableMap()     # bucket is new to the table
    oldsize = len(self._table[j])
    self._table[j][k] = v
    if len(self._table[j]) > oldsize:         # key was new to the table
      self._n += 1                            # increase overall map size

  def _bucket_delitem(self, j, k):
    bucket = self._table[j]
    if bucket is None:
      raise KeyError('Key Error: ' + repr(k))        # no match found
    del bucket[k]                                    # may raise KeyError

  def __iter__(self):
    for bucket in self._table:
      if bucket is not None:                         # a nonempty slot
        for key in bucket:
          yield key

线性探测思想的具体子类实现

# 利用线性探测处理冲突的ProbeHashMap类的具体实现
from .hash_map_base import HashMapBase

class ProbeHashMap(HashMapBase):
  """Hash map implemented with linear probing for collision resolution."""
  _AVAIL = object()       # sentinal marks locations of previous deletions

  def _is_available(self, j):
    """Return True if index j is available in table."""
    return self._table[j] is None or self._table[j] is ProbeHashMap._AVAIL

  def _find_slot(self, j, k):
    """Search for key k in bucket at index j.

    Return (success, index) tuple, described as follows:
    If match was found, success is True and index denotes its location.
    If no match found, success is False and index denotes first available slot.
    """
    firstAvail = None
    while True:                               
      if self._is_available(j):
        if firstAvail is None:
          firstAvail = j                      # mark this as first avail
        if self._table[j] is None:
          return (False, firstAvail)          # search has failed
      elif k == self._table[j]._key:
        return (True, j)                      # found a match
      j = (j + 1) % len(self._table)          # keep looking (cyclically)

  def _bucket_getitem(self, j, k):
    found, s = self._find_slot(j, k)
    if not found:
      raise KeyError('Key Error: ' + repr(k))        # no match found
    return self._table[s]._value

  def _bucket_setitem(self, j, k, v):
    found, s = self._find_slot(j, k)
    if not found:
      self._table[s] = self._Item(k,v)               # insert new item
      self._n += 1                                   # size has increased
    else:
      self._table[s]._value = v                      # overwrite existing

  def _bucket_delitem(self, j, k):
    found, s = self._find_slot(j, k)
    if not found:
      raise KeyError('Key Error: ' + repr(k))        # no match found
    self._table[s] = ProbeHashMap._AVAIL             # mark as vacated

  def __iter__(self):
    for j in range(len(self._table)):                # scan entire table
      if not self._is_available(j):
        yield self._table[j]._key

10.3 有序映射

在这一部分介绍一个称为有序映射的映射ADT的扩展,它包括标准映射的所有行为:
 

M.find_min()用最小键返回(键,值)对
M.find_max()用最大键返回(键,值)对
M.find_lt(k)用严格小于k的最大键返回(键,值)对
M.find_le(k)用严格小于等于k的最大键返回(键,值)对
M.find_gt(k)用严格大于k的最小键返回(键,值)对
M.find_ge(k)用严格大于或等于k的最小键返回(键,值)对
M.find_range(start,stop)用start <=键 <=stop迭代遍历所有(键,值)。如果start指定为None,从最小的键开始迭代;如果stop为None,到最大键迭代结束。
iter(M)根据自然顺序从最小到最大迭代遍历映射中的所有键
reversed(M)根据逆序迭代映射中所有键r.

10.3.1 排序检索表

我们将映射的元组存储在一个基于数组的序列A中,以键的升序排列,假定键是天然定义的顺序,我们称这个映射实现为排序检索表

二分查找和不精确查找

from .map_base import MapBase

class SortedTableMap(MapBase):
  """Map implementation using a sorted table."""

  #----------------------------- nonpublic behaviors -----------------------------
  def _find_index(self, k, low, high):
    """Return index of the leftmost item with key greater than or equal to k.

    Return high + 1 if no such item qualifies.

    That is, j will be returned such that:
       all items of slice table[low:j] have key < k
       all items of slice table[j:high+1] have key >= k
    """
    if high < low:
      return high + 1                               # no element qualifies
    else:
      mid = (low + high) // 2 
      if k == self._table[mid]._key:
        return mid                                  # found exact match
      elif k < self._table[mid]._key:
        return self._find_index(k, low, mid - 1)    # Note: may return mid
      else:
        return self._find_index(k, mid + 1, high)   # answer is right of mid

  #----------------------------- public behaviors -----------------------------
  def __init__(self):
    """Create an empty map."""
    self._table = []

  def __len__(self):     # 1.计算长度
    """Return number of items in the map."""
    return len(self._table)

  def __getitem__(self, k):   # 2. 获得将k的值v
    """Return value associated with key k (raise KeyError if not found)."""
    j = self._find_index(k, 0, len(self._table) - 1)
    if j == len(self._table) or self._table[j]._key != k:
      raise KeyError('Key Error: ' + repr(k))
    return self._table[j]._value
  
  def __setitem__(self, k, v):   # 3 修改或者添加键值对(k,v)
    """Assign value v to key k, overwriting existing value if present."""
    j = self._find_index(k, 0, len(self._table) - 1)
    if j < len(self._table) and self._table[j]._key == k:
      self._table[j]._value = v                         # reassign value
    else:
      self._table.insert(j, self._Item(k,v))            # adds new item
  
  def __delitem__(self, k):      # 4 删除键k的值v 
    """Remove item associated with key k (raise KeyError if not found)."""
    j = self._find_index(k, 0, len(self._table) - 1)
    if j == len(self._table) or self._table[j]._key != k:
      raise KeyError('Key Error: ' + repr(k))
    self._table.pop(j)                                  # delete item
  
  def __iter__(self):            # 5 生成迭代
    """Generate keys of the map ordered from minimum to maximum."""
    for item in self._table:
      yield item._key

  def __reversed__(self):        # 6 逆序
    """Generate keys of the map ordered from maximum to minimum."""
    for item in reversed(self._table):
      yield item._key

  def find_min(self):           # 7 用最小键返回键值对
    """Return (key,value) pair with minimum key (or None if empty)."""
    if len(self._table) > 0:
      return (self._table[0]._key, self._table[0]._value)
    else:
      return None

  def find_max(self):          # 8 用最大键返回键值对
    """Return (key,value) pair with maximum key (or None if empty)."""
    if len(self._table) > 0:
      return (self._table[-1]._key, self._table[-1]._value)
    else:
      return None

  def find_le(self, k):        # 9
    """Return (key,value) pair with greatest key less than or equal to k.

    Return None if there does not exist such a key.
    """
    j = self._find_index(k, 0, len(self._table) - 1)      # j's key >= k
    if j < len(self._table) and self._table[j]._key == k:
      return (self._table[j]._key, self._table[j]._value)      # exact match
    elif j > 0:
      return (self._table[j-1]._key, self._table[j-1]._value)  # Note use of j-1
    else:
      return None

  def find_ge(self, k):    # 10 
    """Return (key,value) pair with least key greater than or equal to k.

    Return None if there does not exist such a key.
    """
    j = self._find_index(k, 0, len(self._table) - 1)      # j's key >= k
    if j < len(self._table):
      return (self._table[j]._key, self._table[j]._value)
    else:
      return None

  def find_lt(self, k):   # 11
    """Return (key,value) pair with greatest key strictly less than k.

    Return None if there does not exist such a key.
    """
    j = self._find_index(k, 0, len(self._table) - 1)      # j's key >= k
    if j > 0:
      return (self._table[j-1]._key, self._table[j-1]._value)  # Note use of j-1
    else:
      return None

  def find_gt(self, k):   # 12 
    """Return (key,value) pair with least key strictly greater than k.

    Return None if there does not exist such a key.
    """
    j = self._find_index(k, 0, len(self._table) - 1)      # j's key >= k
    if j < len(self._table) and self._table[j]._key == k:
      j += 1                                       # advanced past match
    if j < len(self._table):
      return (self._table[j]._key, self._table[j]._value)
    else:
      return None

  def find_range(self, start, stop):  # 13
    """Iterate all (key,value) pairs such that start <= key < stop.

    If start is None, iteration begins with minimum key of map.
    If stop is None, iteration continues through the maximum key of map.
    """
    if start is None:
      j = 0
    else:
      j = self._find_index(start, 0, len(self._table)-1)   # find first result
    while j < len(self._table) and (stop is None or self._table[j]._key < stop):
      yield (self._table[j]._key, self._table[j]._value)
      j += 1

有序映射的空间需求是O(n),时间需求如下:

len(M)O(1)
k in MO(logn)
M[k] = v最坏情况下为O(n),如果存在则O(logn)
del M[k]最坏情况下为O(n)
M.find_min(),M.find_max()O(1)
M.find_lt(k),M.find_gt(k),M.find_le(k),M.find_ge(k)O(logn)
M.find_range(start,stop)O(s + logn),中间有s项
iter(M),reversed(M)O(n)

10.3.2 有序映射的两种应用

航班数据库

最大值集

# 一个使用有序映射维持最大值集的类的实现
from .sorted_table_map import SortedTableMap

class CostPerformanceDatabase:
  """Maintain a database of maximal (cost,performance) pairs."""

  def __init__(self):
    """Create an empty database."""
    self._M = SortedTableMap()             # or a more efficient sorted map

  def best(self, c):
    """Return (cost,performance) pair with largest cost not exceeding c.

    Return None if there is no such pair.
    """
    return self._M.find_le(c)

  def add(self, c, p):
    """Add new entry with cost c and performance p."""
    # determine if (c,p) is dominated by an existing pair
    other = self._M.find_le(c)              # other is at least as cheap as c
    if other is not None and other[1] >= p: # if its performance is as good,
        return                              # (c,p) is dominated, so ignore
    self._M[c] = p                          # else, add (c,p) to database
    # and now remove any pairs that are dominated by (c,p)
    other = self._M.find_gt(c)              # other more expensive than c
    while other is not None and other[1] <= p:
      del self._M[other[0]]
      other = self._M.find_gt(c)

10.4 跳跃表

10.5 集合、多集和多映射

10.5.1 集合的抽象数据类型

基本的五个行为|| 其他行为

S.add(e)S.remove(e),S.pop(), S.clear()
S.discard(e)S == T, S != T, S <= T, S < T, S >= T, S > T, S.isdisjoint(T)
e in SS | T, S |= T, S & T, S &= T, S^T S^=T,S-T,S-=T 
len(S) 
iter(S)

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值