順序表的查找 - 二分查找法


對有序表進行查找運算的時候,可以通過縮減問題的規模,大幅度提高查找效率。

 

 

 首節點 5 的位置為0,尾結點 為 199 的地址為 11;

求和折半后( (11+1)/ 2 )計算出中間位置的地址為 5;

與 位置5 上的元素 43 比較,21 小於 43,因此 21 只能出現在左半段;

縮小查找范圍,舍棄右半段;

重復折半查找的過程:

 

 

計算出中間位置為2 (此處2,指的是下標為2的位置),與 位置2 的 元素12 相比較,21>12。因此 21 只能出現在右半段,舍棄左半段。

現在查找段的首結點地址為3,尾結點地址為4,

 

 

求和折半后,計算出中間位置的地址為 3,與 位置3 上的 元素21 比較,恰好等於待查找元素21,查找成功!

核心思想:① 計算中值位置;② 縮小查找區間。

 left 表示起點,right 表示終點,mid 表示中值點,數組 a 存放元素,x 為待查找元素

left 表示起點
right 表示終點
mid 表示中值點
數組 a[] 存放元素
x 待查找元素

三條語句:

① 計算中值點:mid = (left + right) / 2

② 若 x = a[mid],查找成功,返回mid,結束;

  2.1 若 x < a[mid],則往左縮小查找區間,重復上述過程;

  2.2 若 x > a[mid],則往右縮小查找區間,重復上述過程。

代碼:

int binary_search(int a[],int x,int left,int right){

  int mid;

  mid = ( left + right )/ 2;// 計算中值點

  if( x == a[mid] )// 若 x = a[mid],查找成功,返回mid

    return mid;

  if(x < a[mid] )

    return binary_search(a,x,left,mid - 1);// 問題的規模縮小了,但是問題的性質沒變化,可以采用遞歸的方式,只是查找區間變為了 left ~ mid - 1 階段。

  else

    return binary_search(a,x,mid + 1,right);

}

 

 

例題:用二分法查找元素 83

下標 0 1 2 3 4 5 6 7 8 9 10 11
元素 5 8 12 21 29 43 52 64 70 81 87 199
  left         mid           right

① left = 0,right = 11;

② 計算中值點:mid = (0+11)/ 2;

③ 第一次比較結果:x > a[5] (83 > 43),因此去掉左半段,修改 left 的值 left = mid + 1 = 6,變成如下:

下標 6 7 8 9 10 11
元素 52 64 70 81 87 199
  left = mid + 1 = 6         right

 

 

開始第二次比較:

① left = 6,right = 11;

② 計算中值點:mid =(6 + 11)/ 2 = 8;

③ 第二次比較結果:x > a[8](83 > 70),因此去掉左半段,修改 left 值,left = mid + 1 = 9,變成如下:

下標 9 10 11
元素 81 87 199
  left = mid + 1 = 9   right

 

 

開始第三次比較:

① left = 9,right = 11;

② 計算中值點:mid =(9+11)/ 2 = 10;

③ 第三次比較結果:x < a[10](83 < 87),因此去掉右半段,修改 rigth 值,right = mid - 1 = 9,變成如下:

下標 9 10 11
元素 81 87 199
  right = mid - 1 = 9 mid right

 

下標 9
元素 81
  right = mid - 1 = 9;
left = 9;

 

開始第四次比較:

① left = 9,right = 9;

② 計算中值點:mid =(9+9)/ 2 = 9;

③ 第四次比較結果:x > a[9](83 > 81),因此修改 left 值,left = mid + 1 = 10,當 left > right 時,查找段已經不復存在,換言之,查找失敗。

 

二分查找算法----代碼部分:

① 第一種寫法

int binary_search(int a[],int x,int left,int right){

  int mid;

  if(left > right) return -1;// 查找失敗,返回 -1

  mid  =(left + right)/ 2;

  if( x == a[mid] )

    return mid;// 查找到,返回

  if( x < a[mid] )

    return binary_search(a,x,left,mid - 1);// 左端查找

  return binary_searc(a,x,mid + 1,right);// 右端查找

}

 

② 第二種寫法

int binary_search(int a[],int n,int x){

  int left,right,mid;

  left = 0;right = n-1;// 確定查找段的 起點終點

  while(left <= right){

    mid = (left + right)/  2;

    if(x == a[mid])return mid;// 查找到,返回

    if(x < a[mid])

      right = mid - 1;// 左端查找

    else

      left = mid + 1; // 右端查找

  }

  return -1;

}

 

關於上述兩種算法:

第 ① 種:使用遞歸,代碼更簡潔清晰,可讀性更好。但由於遞歸需要系統堆棧,所以空間消耗要比非遞歸

代碼大很多。而且,如果遞歸深度太大,系統可能撐不住。

第 ② 種:速度快,結構簡單,但可讀性略遜一籌。

二分查找算法的核心思想是 “分而治之” :將一個難以直接解決的大問題,分割成一些規模較小的,性質相同的子問題,以便各個擊破,分而治之。

二分查找算法有兩個前提:① 順序存儲;② 有序表。

 

二分查找法性能分析:

此處借助 “判定樹” 簡要分析一下算法的時間復雜度:

① 以有序數組 a[6] 為例,執行二分算法時

② 首先比較的是下標為 2 的元素 24,mid = (5+0)/  2 = 2 // 注意,此處的 2 ,是下標!

③ 如果查找元素為 24,查找成功!

④ 如果比 24 小,那么再計算出來的 mid 值為 0,mid =(第②步里的 mid - 1)/ 2 =(2-1)/  2 = 0

關於此處mid:開始時 mid =(5+0)/  2 = 2 ,由於是 lift 和 right 都是 int 型,因此計算結果如例所示:

9/2 = 4;7/2 = 3;11/2 = 5;

⑤ 如果查找元素比 14 大,那么再計算出的 mid 值就是 1。// 舍棄 mid 與 mid 左側段落,然后mid + 1 = 1

 

成功的查找:查找路徑終結於結點 i,查找長度 = 結點 i 的層數

判定樹中,24 需要一次比較,14和53 需要兩次比較,17和43和76 需要三次比較,因此查找這個六個結點的平均查找長度為 =(1 + 2 + 2 + 3 + 3 + 3)/  6 = 14/6,括號里的數字,可以這樣理解:

① 假如我們尋找的元素是 24 ,那么開始時,僅尋找一個,就能找到它,因此查找長度為1;

② 假如我們尋找的元素是 14 ,那么開始時,需要途徑兩個元素(包括被尋找元素本身),因此長度為2;

又因為第二層,有兩個元素,也就是 “14” 和 “53”,所以查找長度就是 2 + 2

③ 第三層同理

 

不成功的查找:

 

上圖中用方框表示的結點,就是外結點。查找長度 = 外結點 i 之父的層數;

下面 x 對應的是待查找的元素

圖中 0 灰色方片,對應 x < a[0]  // 假如 x < 14 ,這個不等式可以表達為 x < a[0]

圖中 1 灰色方片,對應 a[0] < x < a[1]  // 假如 14< x < 17,這個不等式可以 表達為 a[0] < x < a[1]

圖中 2 灰色方片,對應 a[1] < x < a[2]  // 同上

 

圖中 5 灰色方片,對應 a[4] < x < a[5]  // 同上

圖中 6 灰色方片,對應 x > a[5]  // 同上

查找不成功的平均查找長度 =(2+3+3+3+3+3+3)/ 7 = 20 / 7 

2: 24 → 14 → 方片0,所以兩次 // 這是 x < 14 的情況;

3: 24 → 14 → 17 → 方片1,所以三次 // 這是 14< x < 17 的情況;

3: 24 → 14 → 17 → 方片2,所以三次 // 這是 17< x < 24 的情況;

3: 24 → 53 → 43 → 方片3,所以三次 // 這是 24< x < 43 的情況;

3: 24 → 53 → 43 → 方片4,所以三次 // 這是 43< x < 53 的情況;

3: 24 → 53 → 76 → 方片5,所以三次 // 這是 53< x < 76 的情況;

3: 24 → 53 → 76 → 方片6,所以三次 // 這是 x > 76 的情況。

因為 查找了 7 次,所以 查找總數 / 7 = 查找不成功的平均查找長度

 

 


免責聲明!

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



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