已有一個由小到大排序的數值陣列,需插入新數值並維持排序順序,且不重新排序整個陣列(O(n) 複雜度)。
利用 PHP 內建 `array_splice` 在指定位置插入,避免手動位移元素。
/**
* 於已排序陣列插入數值,保持升序
* @param array $arr 已排序陣列(傳址修改)
* @param int|float $val 待插入數值
* @return array 插入後的陣列
*/
function sortedInsert(array &$arr, $val): array {
$left = 0;
$right = count($arr) - 1;
$pos = count($arr); // 預設插入尾端
// 二分搜尋找到第一個 >= $val 的位置
while ($left <= $right) {
$mid = (($left + $right) >> 1);
if ($arr[$mid] >= $val) {
$pos = $mid;
$right = $mid - 1;
} else {
$left = $mid + 1;
}
}
array_splice($arr, $pos, 0, [$val]);
return $arr;
}
// 測試
$data = [10, 20, 30, 40, 50];
sortedInsert($data, 35); // [10,20,30,35,40,50]
sortedInsert($data, 5); // [5,10,20,30,35,40,50]
sortedInsert($data, 55); // [5,10,20,30,35,40,50,55]
print_r($data);
| 方法 | 時間複雜度 | 空間複雜度 | 適用場景 |
|---|---|---|---|
| 二分搜尋 + array_splice | O(log n) + O(n) = O(n) | O(1) | 單筆插入、陣列已排序 |
| array_push + sort() | O(n log n) | O(n) | 批次插入後一次排序 |
| 原文迴圈比對法 | O(n) | O(n) | 教學示範,效能較差 |
function sortedInsertEx(array &$arr, $val, callable $cmp = null): array {
$cmp = $cmp ?? fn($a, $b) => $a <=> $b; // PHP 7+ 太空船運算子
$pos = count($arr);
$left = 0;
$right = $pos - 1;
while ($left <= $right) {
$mid = (($left + $right) >> 1);
if ($cmp($arr[$mid], $val) >= 0) {
$pos = $mid;
$right = $mid - 1;
} else {
$left = $mid + 1;
}
}
array_splice($arr, $pos, 0, [$val]);
return $arr;
}
// 物件陣列依屬性排序
$users = [
['id'=>1, 'score'=>80],
['id'=>2, 'score'=>90],
];
sortedInsertEx($users, ['id'=>3, 'score'=>85], fn($a,$b)=>$a['score']<=>$b['score']);
// 結果:80, 85, 90
sort() 一次完成更快。array_is_list() 檢查陣列為列表型,避免關聯陣列誤用。