首頁  >  文章  >  後端開發  >  如何快速取出所對應的值

如何快速取出所對應的值

WBOY
WBOY原創
2016-12-01 01:27:19992瀏覽

一個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的

  1. array_search — 在陣列中搜尋給定的值,如果成功則傳回對應的鍵名

  2. 取得鍵名,$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。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn