一個php的索引數組,數組裡面的值為從1到100之間的整數(不重複),並且數值可能是斷斷續續,也就是可能有就7,9但是沒有8。且順序是打亂的,也就是,不是從1排到100這樣的
現在假設$a=50, 怎樣最快的取出==$a或是跟$a最相鄰的兩個數值呢?
對了其中數組中的值不一定有等於$a的
一個php的索引數組,數組裡面的值為從1到100之間的整數(不重複),並且數值可能是斷斷續續,也就是可能有就7,9但是沒有8。且順序是打亂的,也就是,不是從1排到100這樣的
現在假設$a=50, 怎樣最快的取出==$a或是跟$a最相鄰的兩個數值呢?
對了其中數組中的值不一定有等於$a的
array_search
— 在陣列中搜尋給定的值,如果成功則傳回對應的鍵名
取得鍵名,$arr[$key-1]
, $arr[$key+1]
即可
樓上的已經非常簡單了,不過需要是如果沒有找到這個數,就找最接近的,而原數組順序是打亂的,所以上下兩個並不一定就是最接近的,當然,二分查找也是一種思路,我提供一下自己用演算法的思路,我的想法是先用木桶排序(我目前所了解的在正整數中的排序的最快方式)
<code>//这个是要求的数组 $arr = [1,2,3...]; //填充一个1-100范围的数组 $search_arr = array_fill(0 , 99 , 0); //遍历数组 foreach($search_arr as $k=>$v){ if(in_array($k , $arr)){ ++ $v; } } //此时search_arr数组里面值是1的就是要找的,同时已经排序好了 foreach($search_arr as $k=>$v){ if($v >= 1){ $new_arr[] = $k; } } //此时的new_arr就是一个键从0自增,同时值是排序的数组,然后再结合楼上的写法,便可求出。 </code>
不知道這樣效率怎麼樣
<code>$arr = range(1, 100); // 要求的数组 $target = 10; // 目标值 shuffle($arr); // 打乱顺序 $val_key = array_search($target, $arr); // 测试 $target 不存在的情况去掉以下注释 // array_splice($arr, $val_key, 1); // $val_key = ''; if ($val_key) { echo "这个值是:{$arr[$val_key]}"; } else { sort($arr); foreach ($arr as $key => $value) { if (($value < $target) && ($arr[$key+1] > $target)) { echo "左边:{$value} <br/>"; echo "右边:{$arr[$key+1]}"; exit; } } }</code>
將它排序(升序),複雜度nlogn(一次排序)
然後二分快速定位,複雜度logn(一次查詢)
<code class="php">// 在有序数组$arr中得到大于等于$val的第一个下标 // 如果想要获得离$val最近的值,通过返回值判断 // 如果大于最大的值,返回数组的长度 function binary_search($arr, $val){ $n = count($arr) - 1; $ans = $n + 1; $l = 0; $r = $n; while($l <= $r){ $mid = ($l + $r) >> 1; if($arr[$mid] >= $val){ $ans = $mid; $r = $mid -1; } else $l = $mid + 1; } return $ans; } $arr = [1,5,9,3,8,7,10,12]; sort($arr); foreach($arr as $key => $val){ printf("%d ", $val); } printf("\n"); $search_num = 6; printf("%d\n", binary_search($arr, $search_num));</code>
1-100有100個數,且其值也是1-100,若詢問69所在位置下標,可以以69為中心,二分查找到它附近的點的下標,若某個位置存在數,則標為1,否則標為0,那麼以69為中心,往左邊二分找最長的區間和為0,往右邊二分找最長的區間和為0,快速求區間和可以用了樹狀數組,更新查詢複雜度為logn,新增數的複雜度為logn。
要求和目的:
樹狀數組保存區間標誌和(某個區間的值是否出現),更新和查詢複雜度logn
以某值為中心找出離它最近的值,然後回傳其下標,二分查,複雜度logn
以空間換時間,保存值->下標的映射。
可以在數組末尾添加數,不要求按順序添加
以下程式碼解決以下問題
假設有一個陣列[5,9,3,8,7,10,12]
詢問離12最近的座標,回傳6
詢問離2最近的座標,回傳2
加入一個不重複的數15
加一個不重複的數18
加一個不重複的數16
加一個不重複的數13
詢問離13最近的座標,回傳10
詢問離17最近的座標,回傳9
<code class="php">// 树状数组初始化长度为106,赋空值为0 $arr_bit = array(); for($i = 0;$i <= 105;$i ++){ $arr_bit[$i] = 0; } // 查询1-$x之间的和 function query($x){ global $arr_bit; $sum = 0; while($x > 0){ $sum += $arr_bit[$x]; $x -= $x & -$x; } return $sum; } // 更新第$x位的标志 function add($x, $val){ global $arr_bit; while($x <= 105){ $arr_bit[$x] += $val; $x += $x & -$x; } } $arr = [5,9,3,8,7,10,12]; $arr_tmp = array(); foreach($arr as $key => $val){ $arr_tmp[$val] = $key; printf("%d ",$val); add($val, 1); } printf("\n"); // 查找离某值最近的下标 // 先查找左边 然后再找右边,若不存在,返回-1 function find_val_pos($val){ if($val < 1 || $val > 100){ return -1; } global $arr_tmp; $n = count($arr); $l = 1; $r = $val; $ans_l = -1; // 得到$val左边最靠近的 while($l <= $r){ $mid = ($l + $r) >> 1; // 获得$val到$mid的标志区间和 $mid_val = query($val) - query($mid - 1); // 若标志区间和大于1,记录答案,l往右移继续查 if($mid_val >= 1){ $ans_l = $mid; $l = $mid + 1; } else $r = $mid - 1; } $l = $val; $r = 101; $ans_r = -1; // 得到$val右边最靠近的 while($l <= $r){ $mid = ($l + $r) >> 1; // 获得$mid到$val的标志区间和 $mid_val = query($mid) - query($val - 1); if($mid_val >= 1){ $ans_r = $mid; $r = $mid - 1; } else $l = $mid + 1; } if($ans_l == -1) return $arr_tmp[$ans_r]; elseif ($ans_r == -1) return $arr_tmp[$ans_l]; else { if($val - $ans_l > $ans_r - $val) return $arr_tmp[$ans_r]; else return $arr_tmp[$ans_l]; } } function add_num($val){ if($val < 1 || $val > 100) return false; global $arr_tmp; if(isset($arr_tmp[$val])){ return false; } else { global $arr; $arr_n = count($arr); $arr_tmp[$val] = $arr_n; $arr[$arr_n] = $val; add($val, 1); return true; } } // 查询12最近的坐标 printf("%d\n",find_val_pos(12)); // 结果为6 // 查询2最近的坐标 printf("%d\n",find_val_pos(2)); // 结果为2 add_num(15); // 15位于7 add_num(18); // 18位于8 add_num(16); // 16位于9 add_num(13); // 13位于10 // 查询13最近的坐标 printf("%d\n",find_val_pos(13)); // 结果为10 // 查询17最近的坐标 printf("%d\n",find_val_pos(17)); // 结果为9 // 查询15最近的坐标 printf("%d\n",find_val_pos(15)); // 结果为7 printf("hh\n"); // 查询100最近的坐标 printf("%d\n",find_val_pos(100)); // 结果为8,因为第8个位置是18,是最大的数</code>
需要額外維護一個下標佔用的區間值,然後套一個平衡二元樹,查詢複雜度logn,新增刪除複雜度logn。