日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

王道408数据结构——第七章 查找

發布時間:2023/12/4 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 王道408数据结构——第七章 查找 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、基本概念
  • 二、順序查找(線性查找)
    • 一般線性表的順序查找
    • 有序表的順序查找
  • 二、折半查找(二分查找)
  • 三、分塊查找(索引順序查找)
  • 四、B樹
  • 五、B+樹
  • 六、散列表
    • 構造散列函數
      • 1. 直接定址法
      • 2. 除留取余法
      • 3. 數字分析法
      • 4. 平方取中法
    • 沖突處理
      • 1. 開放定址法
      • 2. 拉鏈法(鏈地址法)
    • 性能分析

一、基本概念

查找:在數據集合中尋找滿足某種條件的數據元素的過程稱為查找。
查找表(查找結構):用于查找的數據集。它由同一類型的數據元素(或記錄)組成,可以是一個數組或鏈表等的數據類型。對查找表的常見操作一般有四種:

  • 查詢某個特定元素是否在查找表中;
  • 檢索滿足條件的某個特定數據元素的各種屬性;
  • 在查找表中插入一個數據元素;
  • 從查找表中刪除某個數據元素。
  • 靜態查找表:若查找表涉及的操作只有上述的1、2,則無序動態地修改查找表。適合靜態查找表的查找方法有順序查找、折半查找、散列查找等。
    動態查找表:若查找表涉及上述所有四個操作,則需要動態插入刪除查找表。適合動態查找表的查找方式有二叉排序樹的查找、二叉平衡樹的查找、B樹的查找、散列查找等。
    關鍵字:數據元素中唯一標識該元素的某個數據項的值。使用基于關鍵字的查找,其查找結果應是唯一的。
    平均查找長度(ASL):在查找過程中,一次查找的長度是指需要比較的關鍵字次數,而平均查找長度是所有查找過程中關鍵字比較次數的平均值。ASL是衡量查找算法效率的最主要指標。

    二、順序查找(線性查找)

    適用于順序表和鏈表。對于順序表,通過數組下標遞增來順序掃描每個元素;對于鏈表,通過next指針來依次掃描每個元素。

    一般線性表的順序查找

    基本思想是從線性表的一端開始,逐個檢查關鍵字是否滿足給定條件。若查找到某個元素的關鍵字滿足條件按,則查找成功,返回該元素在線性表中的位置;若已找到表的另一端,但還沒找到符合條件的元素,則返回查找失敗的信息。
    下面給出算法

    int searchSeq(SSTable ST, ElemType key){// 引入哨兵元素,在創建查找表時,0號單元留空,預留給帶查找元素關鍵字// 引入哨兵元素的目的是使得循環不必判斷數組是否越界,可以避免很多不必要的判斷語句,提高效率ST.elem[0] = key; // 從后往前查找,滿足i==0時,循環一定會跳出for(i = ST.length; ST.elem[i] != key; --i);return i; }

    對于有n個元素的表,定位第 i 個元素時,需進行n-i+1次比較。假設每個元素的查找概率相同,即Pi=1/nP_i=1/nPi?=1/n時,ASL(成功)=∑i=1nPi(n?i+1)=n+12ASL_{(成功)}=\sum_{i=1}^nP_i(n-i+1)=\frac{n+1}{2}ASL()?=i=1n?Pi?(n?i+1)=2n+1?
    查找不成功時,與表中各關鍵字的比較次數為n+1次,ASL(不成功)=n+1ASL_{(不成功)}=n+1ASL()?=n+1

    通常,查找表中記錄的查找概率并不相等,若能預先得知每個記錄的查找概率,則可以先對記錄按查找概率進行排序。

    缺點:當n較大時,ASL較大,效率低。
    優點:對數據元素的儲存沒有要求,順序儲存或鏈式存儲(只能順序查找)皆可。對表中記錄的有序性也沒有要求。

    有序表的順序查找

    若在查找前就知道表的關鍵字是有序的,則可以不用再比較到表的另一端就能確定查找失敗,從而降低失敗的ASL。
    可以用查找判定樹來描述查找過程。樹中圓形結點標識表中存在的元素;舉行結點稱為失敗結點,描述的是不在表中的數據值的集合,若有n個結點,則相應有n+1個失敗結點。

    查找成功的ASL和一般線性表相同。
    查找失敗是,查找指針一定走到了某個失敗結點。失敗結點時虛構的,所以到達失敗結點時的查找長度等于其父結點所在的層數。
    ASL(不成功)=∑j=1nqj(lj?1)=1+2+?+n+nn+1=n2+nn+1ASL_{(不成功)}=\sum_{j=1}^nq_j(l_j-1)=\frac{1+2+\cdots+n+n}{n+1}=\frac{n}{2}+\frac{n}{n+1}ASL()?=j=1n?qj?(lj??1)=n+11+2+?+n+n?=2n?+n+1n?式中,qjq_jqj?是到達第 j 個失敗結點的概率,在相等查找概率的情形下,它為1/(n+1)1/(n+1)1/(n+1)ljl_jlj?是第 j 個失敗結點所在層數。

    二、折半查找(二分查找)

    基本思想:首先將給定值key于表中間位置的元素進行比較,若相等,則查找成功,返回元素位置;若不等,則目標元素只能在中間元素以外的前半部分或后半部分,然后在縮小的范圍內繼續同樣的查找。
    算法如下

    int binnarySearch(SqList L, ElemType key){int low = 0;int high = L.length - 1;int mid;while(low <= high){mid = (low + high) / 2;if(L.elem[mid] == key)return mid;else if(L.elem[mid] < key)low = mid + 1;elsehigh = mid - 1;} return -1; // 查找失敗 }

    折半查找也可用判定樹來描述。每個結點值均大于其左子結點,小于其右子結點。若有序序列有n個元素,則判定樹中有n個非葉結點和n+1個葉節點。
    顯然,二分查找的判定樹是一棵平衡二叉樹

    ASL(成功)=1n∑i=1nli=1n(1×1+2×2+?+h×2h?1)=n+1nlog?2(n+1)?1≈log?2(n+1)?1(n較大時,可近似為)\begin{aligned} ASL_{(成功)}&=\frac{1}{n}\sum_{i=1}^nl_i\\ &=\frac{1}{n}(1\times1+2\times2+\cdots+h\times2^{h-1})\\ &=\frac{n+1}{n}\log_2(n+1)-1\\ &\approx\log_2(n+1)-1(n較大時,可近似為)\\ \end{aligned} ASL()??=n1?i=1n?li?=n1?(1×1+2×2+?+h×2h?1)=nn+1?log2?(n+1)?1log2?(n+1)?1(n)?
    式中,h是樹的高度,元素個數為 n 時h=?log?2(n+1)?h=\lceil\log_2(n+1)\rceilh=?log2?(n+1)?(與n個結點的完全二叉樹相同)
    時間復雜度為O(log?2n)O(\log_2n)O(log2?n),平均情況下比順序查找效率高。
    計算失敗的ASL時,同樣已其父節點的高度作為失敗結點的高度。

    折半查找需要快速定位查找區域,要求線性表必須具有隨機存取的特性。該查找法僅適合于順序存儲結構,且要求元素按關鍵字有序排序。

    三、分塊查找(索引順序查找)

    基本思想:將查找表分為若干子,塊內元素可以無序,但塊之間是有序的,即前一塊中的最大關鍵字小于后一塊中的最小關鍵字。再建立一個索引表,索引表中的每個元素含有各塊中的最大關鍵字和各塊中的第一個元素的地址,索引表按關鍵字有序排序。

    分塊查找吸取了順序查找和折半查找各自的優點,既有動態結構,又適合于快速查找。查找分為兩步,第一步是在索引表中確定待查記錄所在的塊,可以使用順序查找或折半查找;第二部是在塊內順序查找。

    分塊查找的ASL為索引查找和塊內查找的平均長度之和。設索引查找和塊內查找ASL為LIL_ILI?、LS、L_SLS?,則分塊查找ASL=LI+LSASL=L_I+L_SASL=LI?+LS?
    將長度為 n 的查找表均勻分為 b 塊,每塊有 s 個記錄,在等概率情況下:
    若塊內和索引表均采用順序查找,則ASL(成功)=LI+LS=b+12+s+12=s2+2s+n2sASL_{(成功)}=L_I+L_S=\frac{b+1}{2}+\frac{s+1}{2}=\frac{s^2+2s+n}{2s}ASL()?=LI?+LS?=2b+1?+2s+1?=2ss2+2s+n?此時,若s=ns=\sqrt{n}s=n?,則ASL有最小值s=n+1s=\sqrt{n}+1s=n?+1
    若對索引表采用折半查找,則ASL(成功)=LI+LS=?log?2(b+1)?+s+12ASL_{(成功)}=L_I+L_S=\lceil\log_2(b+1)\rceil+\frac{s+1}2ASL()?=LI?+LS?=?log2?(b+1)?+2s+1?

    四、B樹

    不考,暫略。

    五、B+樹

    不考,暫略。

    六、散列表

    散列函數:把查找表中的關鍵字映射成該關鍵字對應地址的函數,記為Addr=Hash(key)。這里的地址可以是數組下標、索引或內存地址等。
    沖突:散列函數可能把兩個不同的關鍵字映射到相同地址,這種情況稱為沖突。發生碰撞的不同關鍵字稱為同義詞。設計好的散列函數應盡可能避免沖突,但沖突總是不可避免的。
    散列表:根據關鍵字直接進行訪問的數據結構。散列表建立了關鍵字和儲存地址的一種直接映射關系

    理想情況下,散列表的查找時間復雜度為O(1)O(1)O(1),即與表中元素的個數無關。

    構造散列函數

    構造散列函數時,必須注意一下幾點:

  • 散列函數的定義域必須包含全部需要儲存的關鍵字,而值域的范圍依賴于散列表的大小。
  • 散列函數計算出來的地址應該能等概率、均勻地分布在整個地址空間中,從而減少沖突的發生。
  • 散列函數應盡量簡單,以減少計算時間。
  • 1. 直接定址法

    直接取關鍵字的某個線性函數值作為散列地址。散列函數為
    H(key)=keyH(key)=keyH(key)=key
    H(key)=a×key+bH(key)=a\times key+bH(key)=a×key+b

    這種方法計算最簡單,且不會產生沖突。
    適合關鍵字分布基本連續的情況,若關鍵字分布不連續,會導致存儲空間的浪費。

    2. 除留取余法

    假定散列表表長為m,取一個不大于m但最接近m的質數p。散列函數為
    H(key)=key%pH(key)=key\%pH(key)=key%p

    這是一種最簡單、最常用的方法。
    關鍵是選好p,使得每個關鍵字通過散列函數轉換后能等概率地映射到散列空間中,從而進坑了減少沖突的可能性。

    3. 數字分析法

    設關鍵字是 r 進制數,而r個數碼在各位上的頻率不一定相同,可能在某些位上分布均勻一些,此時應選取數碼分布較為均勻的若干位作為散列地址。

    這種方法適合已知的關鍵字集合。若更換了關鍵字,則需要重新構造新的散列函數。

    4. 平方取中法

    關鍵字平方值的中間幾位作為散列地址。

    這種方法得到的散列地址與關鍵字的每位都有關系,因此使得散列地址分布比較均勻,適合關鍵字的每位取值都不夠均勻或均小于散列地址所需的位數。

    沖突處理

    任何散列函數都不能絕對避免沖突,必須考慮發生沖突時的處理方法,即為沖突的關鍵字尋找下一個空的hash地址。若得到的另一個散列地址仍然發生沖突,則需要繼續求下一個hash地址。

    1. 開放定址法

    指看存放新表項的空閑地址既向它的同義詞表項開放,又向它的非同義詞表項開發。遞推公式為Hi=(H(key)+di)%mH_i=(H(key)+d_i)\%mHi?=(H(key)+di?)%m式中,HiH_iHi?表示處理沖突第 i 次探測得到的散列地址,m表示散列表表長,did_idi?為增量序列。

    取定某一增量序列did_idi?后,對應的處理方法就是確定的。通常有以下4種取法:

    • 線性探測法(線性探測再散列法):di=0,1,2?,m?1d_i=0,1,2\cdots ,m-1di?=0,1,2?,m?1
      • 沖突發生時,順序查看表中下一個單元,直到找出一個空單元或查遍全表(此時表已滿)。
      • 線性探查法可能使第 i 個散列地址的同義詞存入第 i+1 個散列地址,這樣本應存入第 i+1 個散列地址的元素就要爭奪 i+2 個散列地址。造成大量元素在相鄰的散列地址上聚集(堆積)起來,大大降低了查找效率。
    • 平方探測法(二次探測再散列法):di=02,12,?12,22,?22,?,k2,?k2(k≤m2)d_i=0^2,1^2,-1^2,2^2,-2^2,\cdots,k^2,-k^2(k\leq\frac m2)di?=02,12,?12,22,?22,?,k2,?k2(k2m?)
      • 散列表長度m必須是一個可以表示為4k+3的素數。
      • 是一種處理沖突的較好方法,可以避免出現堆積問題。
      • 缺點是不能探測到散列表上的所有單元,但至少能探測到一半單元。
    • 再散列法(雙散列法):di=Hash2(key)d_i=Hash_2(key)di?=Hash2?(key)
      • 當通過第一個散列函數得到的地址發生沖突時,利用第二個散列函數計算該關鍵字的地址增量
      • 具體散列函數為Hi=(H(key)+i×Hash2(key))%mH_i=(H(key)+i\times Hash_2(key))\%mHi?=(H(key)+i×Hash2?(key))%m
      • 最多經過m-1次探測就會遍歷表中所有位置。
    • 偽隨機序列法:did_idi?為偽隨機數序列。

    在開放定址法中,不能隨便物理刪除表中已有的元素,因為若刪除元素,會截斷其他具有相同散列地址的元素的查找地址。因此,要刪除一個元素時,可以給它添加一個刪除標記,進行邏輯刪除。這種方法需要定期維護散列表,把刪除標記的元素物理刪除。

    2. 拉鏈法(鏈地址法)

    把所有同義詞儲存在一個線性鏈表中。這個線性鏈表由其散列地址唯一標識。
    適合于經常進行插入和刪除的情況。

    性能分析

    散列表的查找過程于構造散列表的過程基本一致。根據散列函數和關鍵字可以計算出記錄的散列地址。若散列地址上:①無記錄,說明查找失敗;②若有記錄且關鍵字相同,則查找成功;③若有記錄但關鍵字不同,使用給定的處理沖突方法計算下一個散列地址,再次進行比較。
    裝填因子:定義為一個表的裝滿程度,即α=表中記錄數n散列表長度m\alpha = \frac{表中記錄數n}{散列表長度m}α=mn?
    散列表的查找效率取決于散列函數、處理沖突的方法和裝填因子。
    散列表的ASL依賴于裝填因子,而不直接依賴于n或m。

    總結

    以上是生活随笔為你收集整理的王道408数据结构——第七章 查找的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。