查找一 線性表的查找


查找的基本概念


 

什么是查找?

查找是根據給定的某個值,在表中確定一個關鍵字的值等於給定值的記錄或數據元素。

 

查找算法的分類

若在查找的同時對表記錄做修改操作(如插入和刪除),則相應的表稱之為動態查找表

否則,稱之為靜態查找表

 

此外,如果查找的全過程都在內存中進行,稱之為內查找

反之,如果查找過程中需要訪問外存,稱之為外查找

 

查找算法性能比較的標准

——平均查找長度ASL(Average Search Length)

由於查找算法的主要運算是關鍵字的比較過程,所以通常把查找過程中對關鍵字需要執行的平均比較長度(也稱為平均比較次數)作為衡量一個查找算法效率優劣的比較標准。

 

 

選取查找算法的因素

(1) 使用什么數據存儲結構(如線性表、樹形表等)。

(2) 表中的次序,即對無序表還是有序表進行查找。

 

順序查找


要點

它是一種最簡單的查找算法,效率也很低下。

 

存儲結構

沒有存儲結構要求,可以無序,也可以有序。

 

基本思想

從數據結構線形表的 一端開始, 順序掃描依次將掃描到的結點關鍵字與給定值k相 比較,若相等則表示查找成功;

若掃描結束仍沒有找到關鍵字等於k的結點,表示查找失敗。

核心代碼

public  int orderSearch( int[] list,  int length,  int key) {
     //  從前往后掃描list數組,如果有元素的值與key相等,直接返回其位置
     for ( int i = 0; i < length; i++) {
         if (key == list[i]) {
             return i;
        }
    }
    
     //  如果掃描完,說明沒有元素的值匹配key,返回-1,表示查找失敗
     return -1;
}

 

算法分析

順序查找算法最好的情況是,第一個記錄即匹配關鍵字,則需要比較 1 次;

最壞的情況是,最后一個記錄匹配關鍵字,則需要比較 N 次。

所以,順序查找算法的平均查找長度為

 

ASL = (N + N-1 + ... + 2 + 1) / N = (N+1) / 2

順序查找的平均時間復雜度O(N)

 

二分查找


要點

二分查找又稱 折半查找,它是一種效率較高的查找方法。

 

存儲結構

使用二分查找需要兩個前提:

(1) 必須是順序存儲結構。

(2) 必須是有序的表。

 

基本思想

首先,將表 中間位置記錄的關鍵字與查找關鍵字比較,如果兩者相等,則查找成功;

否則利用中間位置記錄將表分成前、后兩個子表,如果中間位置記錄的關鍵字大於查找關鍵字,則進一步查找前一子表,否則進一步查找后一子表。
重復以上過程,直到找到滿足條件的記錄,使查找成功,或直到子表不存在為止,此時查找不成功。

核心代碼

public  int binarySearch( int[] list,  int length,  int key) {
     int low = 0, mid = 0, high = length - 1;
     while (low <= high) {
        mid = (low + high) / 2;
         if (list[mid] == key) {
             return mid;  //  查找成功,直接返回位置
        }  else  if (list[mid] < key) {
            low = mid + 1;  //  關鍵字大於中間位置的值,則在大值區間[mid+1, high]繼續查找
        }  else {
            high = mid - 1;  //  關鍵字小於中間位置的值,則在小值區間[low, mid-1]繼續查找
        }
    }
     return -1;
}

 

算法分析

二分查找的過程可看成一個二叉樹

把查找區間的中間位置視為樹的根,左區間和右區間視為根的左子樹和右子樹。

由此得到的二叉樹,稱為二分查找的判定樹或比較樹。

由此可知,二分查找的平均查找長度實際上就是樹的高度O(log2N)

 

分塊查找


要點
分塊查找(Blocking Search)又稱 索引順序查找。它是一種性能介於順序查找和二分查找之間的查找方法。

分塊查找由於只要求索引表是有序的,對塊內節點沒有排序要求,因此特別適合於節點動態變化的情況。

 

存儲結構

分塊查找表是由“分塊有序”的線性表索引表兩部分構成的。

 

所謂“分塊有序”的線性表,是指:

假設要排序的表為R[0...N-1],將表均勻分成b塊,前b-1塊中記錄個數為s=N/b,最后一塊記錄數小於等於s;

每一塊中的關鍵字不一定有序,但前一塊中的最大關鍵字必須小於后一塊中的最小關鍵字

注:這是使用分塊查找的前提條件。

 

如上將表均勻分成b塊后,抽取各塊中的最大關鍵字起始位置構成一個索引表IDX[0...b-1]。

由於表R是分塊有序的,所以索引表是一個遞增有序表

 

下圖就是一個分塊查找表的存儲結構示意圖

基本思想

分塊查找算法有兩個處理步驟:

(1) 首先查找索引表

因為分塊查找表是“分塊有序”的,所以我們可以通過索引表來鎖定關鍵字所在的區間。

又因為索引表是遞增有序的,所以查找索引可以使用順序查找或二分查找。

(2) 然后在已確定的塊中進行順序查找

因為塊中不一定是有序的,所以只能使用順序查找。

代碼范例

 1  public  class BlockSearch {
 2 
 3      class IndexType {
 4          public  int key;  //  分塊中的最大值
 5          public  int link;  //  分塊的起始位置
 6     }
 7 
 8      //  建立索引方法,n 是線性表最大長度,gap是分塊的最大長度
 9      public IndexType[] createIndex( int[] list,  int n,  int gap) {
10          int i = 0, j = 0, max = 0;
11          int num = n / gap;
12         IndexType[] idxGroup =  new IndexType[num];  //  根據步長數分配索引數組大小
13 
14          while (i < num) {
15             j = 0;
16             idxGroup[i] =  new IndexType();
17             idxGroup[i].link = gap * i;  //  確定當前索引組的第一個元素位置
18             max = list[gap * i];  //  每次假設當前組的第一個數為最大值
19               //  遍歷這個分塊,找到最大值
20              while (j < gap) {
21                  if (max < list[gap * i + j]) {
22                     max = list[gap * i + j];
23                 }
24                 j++;
25             }
26             idxGroup[i].key = max;
27             i++;
28         }
29 
30          return idxGroup;
31     }
32 
33      //  分塊查找算法
34      public  int blockSearch(IndexType[] idxGroup,  int m,  int[] list,  int n,  int key) {
35          int mid = 0;
36          int low = 0;
37          int high = m -1;
38          int gap = n / m;  //  分塊大小等於線性表長度除以組數
39          
40           //  先在索引表中進行二分查找,找到的位置存放在 low 中
41          while (low <= high) {
42             mid = (low + high) / 2;
43              if (idxGroup[mid].key >= key) {
44                 high = mid - 1;
45             }  else {
46                 low = mid + 1;
47             }
48         }
49 
50          //  在索引表中查找成功后,再在線性表的指定塊中進行順序查找
51          if (low < m) {
52              for ( int i = idxGroup[low].link; i < idxGroup[low].link + gap; i++) {
53                  if (list[i] == key)
54                      return i;
55             }
56         }
57 
58          return -1;
59     }
60 
61      //  打印完整序列
62      public  void printAll( int[] list) {
63          for ( int value : list) {
64             System.out.print(value + " ");
65         }
66         System.out.println();
67     }
68 
69      //  打印索引表
70      public  void printIDX(IndexType[] list) {
71         System.out.println("構造索引表如下:");
72          for (IndexType elem : list) {
73             System.out.format("key = %d, link = %d\n", elem.key, elem.link);
74         }
75         System.out.println();
76     }
77 
78      public  static  void main(String[] args) {
79          int key = 85;
80          int array[] = { 8, 14, 6, 9, 10, 22, 34, 18, 19, 31, 40, 38, 54, 66, 46, 71, 78, 68, 80, 85 };
81         BlockSearch search =  new BlockSearch();
82         
83         System.out.print("線性表: ");
84         search.printAll(array);
85 
86         IndexType[] idxGroup = search.createIndex(array, array.length, 5);
87         search.printIDX(idxGroup);
88          int pos = search.blockSearch(idxGroup, idxGroup.length, array,
89                 array.length, key);
90          if (-1 == pos) {
91             System.out.format("查找key = %d失敗", key);
92         }  else {
93             System.out.format("查找key = %d成功,位置為%d", key, pos);
94         }
95     }
96 
97 }
分塊查找之JAVA實現

 

運行結果

線性表: 8 14 6 9 10 22 34 18 19 31 40 38 54 66 46 71 78 68 80 85 
構造索引表如下:
key = 14, link = 0
key = 34, link = 5
key = 66, link = 10
key = 85, link = 15

查找key = 85成功,位置為19

 

算法分析

因為分塊查找實際上是兩次查找過程之和。若以二分查找來確定塊,顯然它的查找效率介於順序查找和二分查找之間。

 

三種線性查找的PK


(1) 以平均查找長度而言,二分查找 > 分塊查找 > 順序查找。

(2) 從適用性而言,順序查找無限制條件,二分查找僅適用於有序表,分塊查找要求“分塊有序”。

(3) 從存儲結構而言,順序查找和分塊查找既可用於順序表也可用於鏈表;而二分查找只適用於順序表。

(4) 分塊查找綜合了順序查找和二分查找的優點,既可以較為快速,也能使用動態變化的要求。

 

參考資料


《數據結構習題與解析》(B級第3版)

 

相關閱讀


歡迎閱讀 程序員的內功——算法 系列


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM