欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解常用查找數(shù)據(jù)結(jié)構(gòu)及算法(Python實(shí)現(xiàn))

 更新時(shí)間:2016年12月09日 13:06:38   作者:劉江  
本篇文章主要介紹了Python實(shí)現(xiàn)常用查找數(shù)據(jù)結(jié)構(gòu)及算法,具有一定的參考價(jià)值,有興趣的可以了解一下。

一、基本概念

查找(Searching)就是根據(jù)給定的某個(gè)值,在查找表中確定一個(gè)其關(guān)鍵字等于給定值的數(shù)據(jù)元素(或記錄)。

查找表(Search Table):由同一類型的數(shù)據(jù)元素(或記錄)構(gòu)成的集合

關(guān)鍵字(Key):數(shù)據(jù)元素中某個(gè)數(shù)據(jù)項(xiàng)的值,又稱為鍵值。

主鍵(Primary Key):可唯一地標(biāo)識(shí)某個(gè)數(shù)據(jù)元素或記錄的關(guān)鍵字。

查找表按照操作方式可分為:

  • 靜態(tài)查找表(Static Search Table):只做查找操作的查找表。它的主要操作是:
  • 查詢某個(gè)“特定的”數(shù)據(jù)元素是否在表中
  • 檢索某個(gè)“特定的”數(shù)據(jù)元素和各種屬性
  • 動(dòng)態(tài)查找表(Dynamic Search Table):在查找中同時(shí)進(jìn)行插入或刪除等操作:
  • 查找時(shí)插入數(shù)據(jù)
  • 查找時(shí)刪除數(shù)據(jù)

二、無序表查找

也就是數(shù)據(jù)不排序的線性查找,遍歷數(shù)據(jù)元素。

算法分析:最好情況是在第一個(gè)位置就找到了,此為O(1);最壞情況在最后一個(gè)位置才找到,此為O(n);所以平均查找次數(shù)為(n+1)/2。最終時(shí)間復(fù)雜度為O(n)

# 最基礎(chǔ)的遍歷無序列表的查找算法
# 時(shí)間復(fù)雜度O(n)

def sequential_search(lis, key):
  length = len(lis)
  for i in range(length):
    if lis[i] == key:
      return i
    else:
      return False


if __name__ == '__main__':
  LIST = [1, 5, 8, 123, 22, 54, 7, 99, 300, 222]
  result = sequential_search(LIST, 123)
  print(result)

三、有序表查找

查找表中的數(shù)據(jù)必須按某個(gè)主鍵進(jìn)行某種排序!

1. 二分查找(Binary Search)

算法核心:在查找表中不斷取中間元素與查找值進(jìn)行比較,以二分之一的倍率進(jìn)行表范圍的縮小。

# 針對(duì)有序查找表的二分查找算法
# 時(shí)間復(fù)雜度O(log(n))

def binary_search(lis, key):
  low = 0
  high = len(lis) - 1
  time = 0
  while low < high:
    time += 1
    mid = int((low + high) / 2)
    if key < lis[mid]:
      high = mid - 1
    elif key > lis[mid]:
      low = mid + 1
    else:
      # 打印折半的次數(shù)
      print("times: %s" % time)
      return mid
  print("times: %s" % time)
  return False

if __name__ == '__main__':
  LIST = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
  result = binary_search(LIST, 99)
  print(result)

2. 插值查找

二分查找法雖然已經(jīng)很不錯(cuò)了,但還有可以優(yōu)化的地方。

有的時(shí)候,對(duì)半過濾還不夠狠,要是每次都排除十分之九的數(shù)據(jù)豈不是更好?選擇這個(gè)值就是關(guān)鍵問題,插值的意義就是:以更快的速度進(jìn)行縮減。

插值的核心就是使用公式:

value = (key - list[low])/(list[high] - list[low])

用這個(gè)value來代替二分查找中的1/2。

上面的代碼可以直接使用,只需要改一句。

# 插值查找算法
# 時(shí)間復(fù)雜度O(log(n))

def binary_search(lis, key):
  low = 0
  high = len(lis) - 1
  time = 0
  while low < high:
    time += 1
    # 計(jì)算mid值是插值算法的核心代碼
    mid = low + int((high - low) * (key - lis[low])/(lis[high] - lis[low]))
    print("mid=%s, low=%s, high=%s" % (mid, low, high))
    if key < lis[mid]:
      high = mid - 1
    elif key > lis[mid]:
      low = mid + 1
    else:
      # 打印查找的次數(shù)
      print("times: %s" % time)
      return mid
  print("times: %s" % time)
  return False

if __name__ == '__main__':
  LIST = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
  result = binary_search(LIST, 444)
  print(result)

插值算法的總體時(shí)間復(fù)雜度仍然屬于O(log(n))級(jí)別的。其優(yōu)點(diǎn)是,對(duì)于表內(nèi)數(shù)據(jù)量較大,且關(guān)鍵字分布比較均勻的查找表,使用插值算法的平均性能比二分查找要好得多。反之,對(duì)于分布極端不均勻的數(shù)據(jù),則不適合使用插值算法。

3. 斐波那契查找

由插值算法帶來的啟發(fā),發(fā)明了斐波那契算法。其核心也是如何優(yōu)化那個(gè)縮減速率,使得查找次數(shù)盡量降低。

使用這種算法,前提是已經(jīng)有一個(gè)包含斐波那契數(shù)據(jù)的列表

F = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,...]

# 斐波那契查找算法
# 時(shí)間復(fù)雜度O(log(n))

def fibonacci_search(lis, key):
  # 需要一個(gè)現(xiàn)成的斐波那契列表。其最大元素的值必須超過查找表中元素個(gè)數(shù)的數(shù)值。
  F = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
     233, 377, 610, 987, 1597, 2584, 4181, 6765,
     10946, 17711, 28657, 46368]
  low = 0
  high = len(lis) - 1
  
  # 為了使得查找表滿足斐波那契特性,在表的最后添加幾個(gè)同樣的值
  # 這個(gè)值是原查找表的最后那個(gè)元素的值
  # 添加的個(gè)數(shù)由F[k]-1-high決定
  k = 0
  while high > F[k]-1:
    k += 1
  print(k)
  i = high
  while F[k]-1 > i:
    lis.append(lis[high])
    i += 1
  print(lis)
  
  # 算法主邏輯。time用于展示循環(huán)的次數(shù)。
  time = 0
  while low <= high:
    time += 1
    # 為了防止F列表下標(biāo)溢出,設(shè)置if和else
    if k < 2:
      mid = low
    else:
      mid = low + F[k-1]-1
    
    print("low=%s, mid=%s, high=%s" % (low, mid, high))
    if key < lis[mid]:
      high = mid - 1
      k -= 1
    elif key > lis[mid]:
      low = mid + 1
      k -= 2
    else:
      if mid <= high:
        # 打印查找的次數(shù)
        print("times: %s" % time)
        return mid
      else:
        print("times: %s" % time)
        return high
  print("times: %s" % time)
  return False

if __name__ == '__main__':
  LIST = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
  result = fibonacci_search(LIST, 444)
  print(result)

算法分析:斐波那契查找的整體時(shí)間復(fù)雜度也為O(log(n))。但就平均性能,要優(yōu)于二分查找。但是在最壞情況下,比如這里如果key為1,則始終處于左側(cè)半?yún)^(qū)查找,此時(shí)其效率要低于二分查找。

總結(jié):二分查找的mid運(yùn)算是加法與除法,插值查找則是復(fù)雜的四則運(yùn)算,而斐波那契查找只是最簡(jiǎn)單的加減運(yùn)算。在海量數(shù)據(jù)的查找中,這種細(xì)微的差別可能會(huì)影響最終的查找效率。因此,三種有序表的查找方法本質(zhì)上是分割點(diǎn)的選擇不同,各有優(yōu)劣,應(yīng)根據(jù)實(shí)際情況進(jìn)行選擇。

四、線性索引查找

對(duì)于海量的無序數(shù)據(jù),為了提高查找速度,一般會(huì)為其構(gòu)造索引表。

索引就是把一個(gè)關(guān)鍵字與它相對(duì)應(yīng)的記錄進(jìn)行關(guān)聯(lián)的過程。

一個(gè)索引由若干個(gè)索引項(xiàng)構(gòu)成,每個(gè)索引項(xiàng)至少包含關(guān)鍵字和其對(duì)應(yīng)的記錄在存儲(chǔ)器中的位置等信息。

索引按照結(jié)構(gòu)可以分為:線性索引、樹形索引和多級(jí)索引。

線性索引:將索引項(xiàng)的集合通過線性結(jié)構(gòu)來組織,也叫索引表。

線性索引可分為:稠密索引、分塊索引和倒排索引

1.稠密索引

稠密索引指的是在線性索引中,為數(shù)據(jù)集合中的每個(gè)記錄都建立一個(gè)索引項(xiàng)。

這其實(shí)就相當(dāng)于給無序的集合,建立了一張有序的線性表。其索引項(xiàng)一定是按照關(guān)鍵碼進(jìn)行有序的排列。

這也相當(dāng)于把查找過程中需要的排序工作給提前做了。

1.分塊索引

給大量的無序數(shù)據(jù)集合進(jìn)行分塊處理,使得塊內(nèi)無序,塊與塊之間有序。

這其實(shí)是有序查找和無序查找的一種中間狀態(tài)或者說妥協(xié)狀態(tài)。因?yàn)閿?shù)據(jù)量過大,建立完整的稠密索引耗時(shí)耗力,占用資源過多;但如果不做任何排序或者索引,那么遍歷的查找也無法接受,只能折中,做一定程度的排序或索引。

分塊索引的效率比遍歷查找的O(n)要高一些,但與二分查找的O(logn)還是要差不少。

1.倒排索引

不是由記錄來確定屬性值,而是由屬性值來確定記錄的位置,這種被稱為倒排索引。其中記錄號(hào)表存儲(chǔ)具有相同次關(guān)鍵字的所有記錄的地址或引用(可以是指向記錄的指針或該記錄的主關(guān)鍵字)。

倒排索引是最基礎(chǔ)的搜索引擎索引技術(shù)。

五、二叉排序樹

二叉排序樹又稱為二叉查找樹。它或者是一顆空樹,或者是具有下列性質(zhì)的二叉樹:

  • 若它的左子樹不為空,則左子樹上所有節(jié)點(diǎn)的值均小于它的根結(jié)構(gòu)的值;
  • 若它的右子樹不為空,則右子樹上所有節(jié)點(diǎn)的值均大于它的根結(jié)構(gòu)的值;
  • 它的左、右子樹也分別為二叉排序樹。

構(gòu)造一顆二叉排序樹的目的,往往不是為了排序,而是為了提高查找和插入刪除關(guān)鍵字的速度。

二叉排序樹的操作:

1.查找:對(duì)比節(jié)點(diǎn)的值和關(guān)鍵字,相等則表明找到了;小了則往節(jié)點(diǎn)的左子樹去找,大了則往右子樹去找,這么遞歸下去,最后返回布爾值或找到的節(jié)點(diǎn)。

2.插入:從根節(jié)點(diǎn)開始逐個(gè)與關(guān)鍵字進(jìn)行對(duì)比,小了去左邊,大了去右邊,碰到子樹為空的情況就將新的節(jié)點(diǎn)鏈接。

3.刪除:如果要?jiǎng)h除的節(jié)點(diǎn)是葉子,直接刪;如果只有左子樹或只有右子樹,則刪除節(jié)點(diǎn)后,將子樹鏈接到父節(jié)點(diǎn)即可;如果同時(shí)有左右子樹,則可以將二叉排序樹進(jìn)行中序遍歷,取將要被刪除的節(jié)點(diǎn)的前驅(qū)或者后繼節(jié)點(diǎn)替代這個(gè)被刪除的節(jié)點(diǎn)的位置。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liu Jiang
# Python 3.5


class BSTNode:
  """
  定義一個(gè)二叉樹節(jié)點(diǎn)類。
  以討論算法為主,忽略了一些諸如對(duì)數(shù)據(jù)類型進(jìn)行判斷的問題。
  """
  def __init__(self, data, left=None, right=None):
    """
    初始化
    :param data: 節(jié)點(diǎn)儲(chǔ)存的數(shù)據(jù)
    :param left: 節(jié)點(diǎn)左子樹
    :param right: 節(jié)點(diǎn)右子樹
    """
    self.data = data
    self.left = left
    self.right = right


class BinarySortTree:
  """
  基于BSTNode類的二叉排序樹。維護(hù)一個(gè)根節(jié)點(diǎn)的指針。
  """
  def __init__(self):
    self._root = None

  def is_empty(self):
    return self._root is None

  def search(self, key):
    """
    關(guān)鍵碼檢索
    :param key: 關(guān)鍵碼
    :return: 查詢節(jié)點(diǎn)或None
    """
    bt = self._root
    while bt:
      entry = bt.data
      if key < entry:
        bt = bt.left
      elif key > entry:
        bt = bt.right
      else:
        return entry
    return None

  def insert(self, key):
    """
    插入操作
    :param key:關(guān)鍵碼 
    :return: 布爾值
    """
    bt = self._root
    if not bt:
      self._root = BSTNode(key)
      return
    while True:
      entry = bt.data
      if key < entry:
        if bt.left is None:
          bt.left = BSTNode(key)
          return
        bt = bt.left
      elif key > entry:
        if bt.right is None:
          bt.right = BSTNode(key)
          return
        bt = bt.right
      else:
        bt.data = key
        return

  def delete(self, key):
    """
    二叉排序樹最復(fù)雜的方法
    :param key: 關(guān)鍵碼
    :return: 布爾值
    """
    p, q = None, self._root   # 維持p為q的父節(jié)點(diǎn),用于后面的鏈接操作
    if not q:
      print("空樹!")
      return
    while q and q.data != key:
      p = q
      if key < q.data:
        q = q.left
      else:
        q = q.right
      if not q:        # 當(dāng)樹中沒有關(guān)鍵碼key時(shí),結(jié)束退出。
        return
    # 上面已將找到了要?jiǎng)h除的節(jié)點(diǎn),用q引用。而p則是q的父節(jié)點(diǎn)或者None(q為根節(jié)點(diǎn)時(shí))。
    if not q.left:
      if p is None:
        self._root = q.right
      elif q is p.left:
        p.left = q.right
      else:
        p.right = q.right
      return
    # 查找節(jié)點(diǎn)q的左子樹的最右節(jié)點(diǎn),將q的右子樹鏈接為該節(jié)點(diǎn)的右子樹
    # 該方法可能會(huì)增大樹的深度,效率并不算高??梢栽O(shè)計(jì)其它的方法。
    r = q.left
    while r.right:
      r = r.right
    r.right = q.right
    if p is None:
      self._root = q.left
    elif p.left is q:
      p.left = q.left
    else:
      p.right = q.left

  def __iter__(self):
    """
    實(shí)現(xiàn)二叉樹的中序遍歷算法,
    展示我們創(chuàng)建的二叉排序樹.
    直接使用python內(nèi)置的列表作為一個(gè)棧。
    :return: data
    """
    stack = []
    node = self._root
    while node or stack:
      while node:
        stack.append(node)
        node = node.left
      node = stack.pop()
      yield node.data
      node = node.right


if __name__ == '__main__':
  lis = [62, 58, 88, 48, 73, 99, 35, 51, 93, 29, 37, 49, 56, 36, 50]
  bs_tree = BinarySortTree()
  for i in range(len(lis)):
    bs_tree.insert(lis[i])
  # bs_tree.insert(100)
  bs_tree.delete(58)
  for i in bs_tree:
    print(i, end=" ")
  # print("\n", bs_tree.search(4))

二叉排序樹總結(jié):

  • 二叉排序樹以鏈?zhǔn)竭M(jìn)行存儲(chǔ),保持了鏈接結(jié)構(gòu)在插入和刪除操作上的優(yōu)點(diǎn)。
  • 在極端情況下,查詢次數(shù)為1,但最大操作次數(shù)不會(huì)超過樹的深度。也就是說,二叉排序樹的查找性能取決于二叉排序樹的形狀,也就引申出了后面的平衡二叉樹。
  • 給定一個(gè)元素集合,可以構(gòu)造不同的二叉排序樹,當(dāng)它同時(shí)是一個(gè)完全二叉樹的時(shí)候,查找的時(shí)間復(fù)雜度為O(log(n)),近似于二分查找。
  • 當(dāng)出現(xiàn)最極端的斜樹時(shí),其時(shí)間復(fù)雜度為O(n),等同于順序查找,效果最差。

六、 平衡二叉樹

平衡二叉樹(AVL樹,發(fā)明者的姓名縮寫):一種高度平衡的排序二叉樹,其每一個(gè)節(jié)點(diǎn)的左子樹和右子樹的高度差最多等于1。

平衡二叉樹首先必須是一棵二叉排序樹!

平衡因子(Balance Factor):將二叉樹上節(jié)點(diǎn)的左子樹深度減去右子樹深度的值。

對(duì)于平衡二叉樹所有包括分支節(jié)點(diǎn)和葉節(jié)點(diǎn)的平衡因子只可能是-1,0和1,只要有一個(gè)節(jié)點(diǎn)的因子不在這三個(gè)值之內(nèi),該二叉樹就是不平衡的。

最小不平衡子樹:距離插入結(jié)點(diǎn)最近的,且平衡因子的絕對(duì)值大于1的節(jié)點(diǎn)為根的子樹。

平衡二叉樹的構(gòu)建思想:每當(dāng)插入一個(gè)新結(jié)點(diǎn)時(shí),先檢查是否破壞了樹的平衡性,若有,找出最小不平衡子樹。在保持二叉排序樹特性的前提下,調(diào)整最小不平衡子樹中各結(jié)點(diǎn)之間的連接關(guān)系,進(jìn)行相應(yīng)的旋轉(zhuǎn),成為新的平衡子樹。

下面是由[1,2,3,4,5,6,7,10,9]構(gòu)建平衡二叉樹







七、多路查找樹(B樹)

多路查找樹(muitl-way search tree):其每一個(gè)節(jié)點(diǎn)的孩子可以多于兩個(gè),且每一個(gè)結(jié)點(diǎn)處可以存儲(chǔ)多個(gè)元素。
 對(duì)于多路查找樹,每個(gè)節(jié)點(diǎn)可以存儲(chǔ)多少個(gè)元素,以及它的孩子數(shù)的多少是關(guān)鍵,常用的有這4種形式:2-3樹、2-3-4樹、B樹和B+樹。

2-3樹

2-3樹:每個(gè)結(jié)點(diǎn)都具有2個(gè)孩子,或者3個(gè)孩子,或者沒有孩子。

一個(gè)2結(jié)點(diǎn)包含一個(gè)元素和兩個(gè)孩子(或者沒有孩子,不能只有一個(gè)孩子)。與二叉排序樹類似,其左子樹包含的元素都小于該元素,右子樹包含的元素都大于該元素。

一個(gè)3結(jié)點(diǎn)包含兩個(gè)元素和三個(gè)孩子(或者沒有孩子,不能只有一個(gè)或兩個(gè)孩子)。

2-3樹中所有的葉子都必須在同一層次上。

其插入操作如下:




 其刪除操作如下:








2-3-4樹

其實(shí)就是2-3樹的擴(kuò)展,包括了4結(jié)點(diǎn)的使用。一個(gè)4結(jié)點(diǎn)包含小中大三個(gè)元素和四個(gè)孩子(或沒有孩子)。

其插入操作:

其刪除操作:

B樹

B樹是一種平衡的多路查找樹。節(jié)點(diǎn)最大的孩子數(shù)目稱為B樹的階(order)。2-3樹是3階B樹,2-3-4是4階B樹。

B樹的數(shù)據(jù)結(jié)構(gòu)主要用在內(nèi)存和外部存儲(chǔ)器的數(shù)據(jù)交互中。



B+樹

為了解決B樹的所有元素遍歷等基本問題,在原有的結(jié)構(gòu)基礎(chǔ)上,加入新的元素組織方式后,形成了B+樹。

B+樹是應(yīng)文件系統(tǒng)所需而出現(xiàn)的一種B樹的變形樹,嚴(yán)格意義上將,它已經(jīng)不是最基本的樹了。

B+樹中,出現(xiàn)在分支節(jié)點(diǎn)中的元素會(huì)被當(dāng)做他們?cè)谠摲种Ч?jié)點(diǎn)位置的中序后繼者(葉子節(jié)點(diǎn))中再次列出。另外,每一個(gè)葉子節(jié)點(diǎn)都會(huì)保存一個(gè)指向后一葉子節(jié)點(diǎn)的指針。

所有的葉子節(jié)點(diǎn)包含全部的關(guān)鍵字的信息,及相關(guān)指針,葉子節(jié)點(diǎn)本身依關(guān)鍵字的大小自小到大順序鏈接

B+樹的結(jié)構(gòu)特別適合帶有范圍的查找。比如查找年齡在20~30歲之間的人。

八、散列表(哈希表)

散列表:所有的元素之間沒有任何關(guān)系。元素的存儲(chǔ)位置,是利用元素的關(guān)鍵字通過某個(gè)函數(shù)直接計(jì)算出來的。這個(gè)一一對(duì)應(yīng)的關(guān)系函數(shù)稱為散列函數(shù)或Hash函數(shù)。

采用散列技術(shù)將記錄存儲(chǔ)在一塊連續(xù)的存儲(chǔ)空間中,稱為散列表或哈希表(Hash Table)。關(guān)鍵字對(duì)應(yīng)的存儲(chǔ)位置,稱為散列地址。

散列表是一種面向查找的存儲(chǔ)結(jié)構(gòu)。它最適合求解的問題是查找與給定值相等的記錄。但是對(duì)于某個(gè)關(guān)鍵字能對(duì)應(yīng)很多記錄的情況就不適用,比如查找所有的“男”性。也不適合范圍查找,比如查找年齡20~30之間的人。排序、最大、最小等也不合適。

因此,散列表通常用于關(guān)鍵字不重復(fù)的數(shù)據(jù)結(jié)構(gòu)。比如python的字典數(shù)據(jù)類型。

設(shè)計(jì)出一個(gè)簡(jiǎn)單、均勻、存儲(chǔ)利用率高的散列函數(shù)是散列技術(shù)中最關(guān)鍵的問題。

 但是,一般散列函數(shù)都面臨著沖突的問題。

沖突:兩個(gè)不同的關(guān)鍵字,通過散列函數(shù)計(jì)算后結(jié)果卻相同的現(xiàn)象。collision。

8.1 散列函數(shù)的構(gòu)造方法

好的散列函數(shù):計(jì)算簡(jiǎn)單、散列地址分布均勻

1.直接定址法

 例如取關(guān)鍵字的某個(gè)線性函數(shù)為散列函數(shù):

f(key) = a*key + b (a,b為常數(shù))

2.數(shù)字分析法

 抽取關(guān)鍵字里的數(shù)字,根據(jù)數(shù)字的特點(diǎn)進(jìn)行地址分配

3.平方取中法 

將關(guān)鍵字的數(shù)字求平方,再截取部分

4.折疊法

 將關(guān)鍵字的數(shù)字分割后分別計(jì)算,再合并計(jì)算,一種玩弄數(shù)字的手段。

5.除留余數(shù)法

 最為常見的方法之一。

 對(duì)于表長(zhǎng)為m的數(shù)據(jù)集合,散列公式為:

f(key) = key mod p (p<=m)

 mod:取模(求余數(shù))

 該方法最關(guān)鍵的是p的選擇,而且數(shù)據(jù)量較大的時(shí)候,沖突是必然的。一般會(huì)選擇接近m的質(zhì)數(shù)。

6.隨機(jī)數(shù)法

 選擇一個(gè)隨機(jī)數(shù),取關(guān)鍵字的隨機(jī)函數(shù)值為它的散列地址。

f(key) = random(key)

總結(jié),實(shí)際情況下根據(jù)不同的數(shù)據(jù)特性采用不同的散列方法,考慮下面一些主要問題:

  • 計(jì)算散列地址所需的時(shí)間
  • 關(guān)鍵字的長(zhǎng)度
  • 散列表的大小
  • 關(guān)鍵字的分布情況
  • 記錄查找的頻率

8.2 處理散列沖突

1、開放定址法

就是一旦發(fā)生沖突,就去尋找下一個(gè)空的散列地址,只要散列表足夠大,空的散列地址總能找到,并將記錄存入。

公式是:

 

這種簡(jiǎn)單的沖突解決辦法被稱為線性探測(cè),無非就是自家的坑被占了,就逐個(gè)拜訪后面的坑,有空的就進(jìn),也不管這個(gè)坑是不是后面有人預(yù)定了的。

 線性探測(cè)帶來的最大問題就是沖突的堆積,你把別人預(yù)定的坑占了,別人也就要像你一樣去找坑。

改進(jìn)的辦法有二次方探測(cè)法和隨機(jī)數(shù)探測(cè)法。

2、再散列函數(shù)法

發(fā)生沖突時(shí)就換一個(gè)散列函數(shù)計(jì)算,總會(huì)有一個(gè)可以把沖突解決掉,它能夠使得關(guān)鍵字不產(chǎn)生聚集,但相應(yīng)地增加了計(jì)算的時(shí)間。

3、鏈接地址法
 碰到?jīng)_突時(shí),不更換地址,而是將所有關(guān)鍵字為同義詞的記錄存儲(chǔ)在一個(gè)鏈表里,在散列表中只存儲(chǔ)同義詞子表的頭指針,如下圖:

這樣的好處是,不怕沖突多;缺點(diǎn)是降低了散列結(jié)構(gòu)的隨機(jī)存儲(chǔ)性能。本質(zhì)是用單鏈表結(jié)構(gòu)輔助散列結(jié)構(gòu)的不足。

4、公共溢出區(qū)法

其實(shí)就是為所有的沖突,額外開辟一塊存儲(chǔ)空間。如果相對(duì)基本表而言,沖突的數(shù)據(jù)很少的時(shí)候,使用這種方法比較合適。

8.3 散列表查找實(shí)現(xiàn)

下面是一段簡(jiǎn)單的實(shí)現(xiàn)代碼:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liu Jiang
# Python 3.5
# 忽略了對(duì)數(shù)據(jù)類型,元素溢出等問題的判斷。


class HashTable:
  def __init__(self, size):
    self.elem = [None for i in range(size)] # 使用list數(shù)據(jù)結(jié)構(gòu)作為哈希表元素保存方法
    self.count = size # 最大表長(zhǎng)

  def hash(self, key):
    return key % self.count # 散列函數(shù)采用除留余數(shù)法

  def insert_hash(self, key):
    """插入關(guān)鍵字到哈希表內(nèi)"""
    address = self.hash(key) # 求散列地址
    while self.elem[address]: # 當(dāng)前位置已經(jīng)有數(shù)據(jù)了,發(fā)生沖突。
      address = (address+1) % self.count # 線性探測(cè)下一地址是否可用
    self.elem[address] = key # 沒有沖突則直接保存。

  def search_hash(self, key):
    """查找關(guān)鍵字,返回布爾值"""
    star = address = self.hash(key)
    while self.elem[address] != key:
      address = (address + 1) % self.count
      if not self.elem[address] or address == star: # 說明沒找到或者循環(huán)到了開始的位置
        return False
    return True


if __name__ == '__main__':
  list_a = [12, 67, 56, 16, 25, 37, 22, 29, 15, 47, 48, 34]
  hash_table = HashTable(12)
  for i in list_a:
    hash_table.insert_hash(i)

  for i in hash_table.elem:
    if i:
      print((i, hash_table.elem.index(i)), end=" ")
  print("\n")

  print(hash_table.search_hash(15))
  print(hash_table.search_hash(33))

8.4 散列表查找性能分析

如果沒發(fā)生沖突,則其查找時(shí)間復(fù)雜度為O(1),屬于最極端的好了。

但是,現(xiàn)實(shí)中沖突可不可避免的,下面三個(gè)方面對(duì)查找性能影響較大:

  • 散列函數(shù)是否均勻
  • 處理沖突的辦法
  • 散列表的裝填因子(表內(nèi)數(shù)據(jù)裝滿的程度)

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解Python 模擬實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式的實(shí)例

    詳解Python 模擬實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式的實(shí)例

    這篇文章主要介紹了詳解Python 模擬實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式的實(shí)例的相關(guān)資料,這里使用了線程知識(shí),隊(duì)列知識(shí)及循環(huán)的知識(shí),需要的朋友可以參考下
    2017-08-08
  • 基于Python編寫微信清理工具的示例代碼

    基于Python編寫微信清理工具的示例代碼

    這篇文章主要和大家分享一個(gè)用Python語言編寫的微信清理小工具的示例代碼,而且該工具不會(huì)刪除文字的聊天記錄,感興趣的可以了解一下
    2022-05-05
  • python使用參數(shù)對(duì)嵌套字典進(jìn)行取值的方法

    python使用參數(shù)對(duì)嵌套字典進(jìn)行取值的方法

    這篇文章主要介紹了python使用參數(shù)對(duì)嵌套字典進(jìn)行取值,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-04-04
  • Python通過select實(shí)現(xiàn)異步IO的方法

    Python通過select實(shí)現(xiàn)異步IO的方法

    這篇文章主要介紹了Python通過select實(shí)現(xiàn)異步IO的方法,實(shí)例分析了Python中select模塊的使用技巧,需要的朋友可以參考下
    2015-06-06
  • Python撲克牌21點(diǎn)游戲?qū)嵗a

    Python撲克牌21點(diǎn)游戲?qū)嵗a

    大家好,本篇文章主要講的是Python撲克牌21點(diǎn)游戲?qū)嵗a,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Python3 max()函數(shù)基礎(chǔ)用法

    Python3 max()函數(shù)基礎(chǔ)用法

    在本篇文章中我們給大家講述了關(guān)于Python3 max()函數(shù)的基本用法以及相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們學(xué)習(xí)下。
    2019-02-02
  • python中實(shí)現(xiàn)字符串翻轉(zhuǎn)的方法

    python中實(shí)現(xiàn)字符串翻轉(zhuǎn)的方法

    這篇文章主要介紹了python中實(shí)現(xiàn)字符串翻轉(zhuǎn)的方法,代碼很簡(jiǎn)單,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-07-07
  • 詳解django+django-celery+celery的整合實(shí)戰(zhàn)

    詳解django+django-celery+celery的整合實(shí)戰(zhàn)

    這篇文章主要介紹了詳解django+django-celery+celery的整合實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • python實(shí)現(xiàn)滑雪游戲

    python實(shí)現(xiàn)滑雪游戲

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)滑雪游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 對(duì)Python 3.2 迭代器的next函數(shù)實(shí)例講解

    對(duì)Python 3.2 迭代器的next函數(shù)實(shí)例講解

    今天小編就為大家分享一篇對(duì)Python 3.2 迭代器的next函數(shù)實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10

最新評(píng)論