插值查詢演算法
-
對於有序的數列[陣列]來說,查詢一個具體的數值[陣列的value],可以使用順序查詢,也可以使用前一章節學習的折半[二分法]查詢。下面有一個具體的例項:
$arr = [1,2,3,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
這是一個分佈均勻的有序陣列,這次需要查詢陣列的值為1的鍵,或者是一個值為20
的鍵。首先使用順序查詢: -
順序查詢:當
findValue = 1
只需要一次,當findValue = 20
就需要差不多二十幾次才能找到,可以說是相當的不穩定 -
折半查詢:當
findValue = 1
如圖:
- 折半查詢:當
findValue = 20
如圖:
這裡可以看到折半查詢需要遞迴五次才能找到需要的數字,很費事很費力,那麼在這種分佈均勻的陣列中查詢一個數值有沒有一個討喜的方法呢,答案是有的:
- 折半查詢的公式
$middle = $leftIndex + ($rightIndex - $leftIndex) / 2
$middle 代表陣列的中間值的鍵, $leftIndex 代表陣列的最小索引, $rightIndex 代表陣列的最大索引
- 插值查詢的公式
$midlle = $leftIndex + ($rightIndex - $leftIndex) * ($findValue - $data[$leftIndex]) / ($data[$rightIndex] - $data[$leftIndex])
$findValue 代表需要查詢的值,$data代表的是有序的陣列,$midlle,$leftIndex,$rightIndex和上面代表的意思一樣
下面是具體的程式碼實現:測試發現,對於分佈均勻的陣列中,查詢某一個數值,幾乎就是一次就可以找到,很神奇吧.最大的功臣就屬於上面的那個公式了。
<?php
/**
* Notes:
* File name:${fILE_NAME}
* Create by: Jay.Li
* Created on: 2019/12/13 0013 11:40
*/
class SearchValue
{
protected static $num = 0;
public function findValue1(array $data, int $leftIndex, int $rightIndex, int $findValue)
{
var_dump("迴圈次數:" . ++self::$num);
if (!is_array($data)) {
return ['code' => -1, 'message' => sprintf("$data 非陣列")];
}
if ($leftIndex > $rightIndex) {
return ['code' => -2, 'message' => sprintf("此 [ %d ] 不在 $data 中", $findValue)];
}
$middleIndex = $leftIndex + ($rightIndex - $leftIndex) * ($findValue - $data[$leftIndex]) / ($data[$rightIndex] - $data[$leftIndex]);
$middleIndex = intval($middleIndex);
if ($middleIndex < $leftIndex || $middleIndex > $rightIndex) {
return ['code' => -3, 'message' => sprintf("中間值的索引 [ %d ] 越界", $middleIndex)];
}
$middleValue = $data[$middleIndex];
if ($findValue < $middleValue) {
return $this->findValue1($data, $leftIndex, $middleIndex - 1, $findValue);
} elseif ($findValue > $middleValue) {
return $this->findValue1($data, $middleIndex +1, $rightIndex, $findValue);
} else {
return $middleValue;
}
}
}
$obj = new SearchValue();
$data = [1,2,3,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var_dump($obj->findValue1($data, 0, count($data) - 1, 20));
本作品採用《CC 協議》,轉載必須註明作者和本文連結
LIYi ---- github地址