插入查找
<?php
/**
* 插入查询
*
* -------------------------------------------------------------
* 思路分析:对于数组长度比较大,关键字分布又是比较均匀的来说,插值查找的效率比折半查找的效率高
* -------------------------------------------------------------
* 它是二分查找的改进。
* 在英文词典里查找“apple”,你下意识里翻开词典是翻前面的书页还是后面的书页呢?如果再查“zoo”,你又会怎么查?
* 显然你不会从词典中间开始查起,而是有一定目的地往前或往后翻。
*
*/
/**
* insertQuery
*
* @param array $container
* @param $num
* @return bool|float|int
*/
function insertQuery(array $container, $num)
{
$count = count($container);
$lower = 0;
$high = $count - 1;
while ($lower <= $high) {
if ($container[ $lower ] == $num) {
return $lower;
}
if ($container[ $high ] == $num) {
return $high;
}
$left = intval($lower + $num - $container[ $lower ]);
$right = ($container[ $high ] - $container[ $lower ]) * ($high - $lower);
$middle = $left /$right;
if ($num < $container[ $middle ]) {
$high = $middle - 1;
} else if ($num > $container[ $middle ]) {
$lower = $middle + 1;
} else {
return $middle;
}
}
return false;
}
// +--------------------------------------------------------------------
// | 方案测试 | php `this.php` || PHPStorm -> 右键 -> Run `this.php`
// +--------------------------------------------------------------------
echo insertQuery([4, 5, 7, 8, 9, 10, 8], 8);
// 6
快速查找
/**
* 快速查询
*
* -------------------------------------------------------------
* 思路分析:数组中间的值floor((low+top)/2)
* -------------------------------------------------------------
* 重复第二步操作直至找出目标数字
*/
/**
* QuickQuery
*
* @param $array
* @param $k
* @param int $low
* @param int $high
* @return int
*/
function QuickQuery($array, $k, $low = 0, $high = 0)
{
//判断是否为第一次调用
if (count($array) != 0 and $high == 0) {
$high = count($array);
}
//如果还存在剩余的数组元素
if ($low <= $high) {
//取$low和$high的中间值
$mid = intval(($low + $high) / 2);
//如果找到则返回
if ($array[$mid] == $k) {
return $mid;
} else if ($k < $array[$mid]) {//如果没有找到,则继续查找
return QuickQuery($array, $k, $low, $mid - 1);
} else {
return QuickQuery($array, $k, $mid + 1, $high);
}
}
return -1;
}
echo QuickQuery([4, 5, 7, 8, 9, 10, 8], 8);
斐波那契查找
<?php
/**
* 斐波那契查询
*
* -------------------------------------------------------------
* 思路分析:斐波那契查找 利用黄金分割原理
* -------------------------------------------------------------
* $num == $container[$mid],直接返回
* $num < $container[$mid],新范围是第 $low 个到 $mid-1 个,此时范围个数为 produced($key-1)-1 个
* $num > $container[$mid],新范围是第 $mid+1 个到 $high 个,此时范围个数为 produced($key-2)-1 个
*/
// +--------------------------------------------------------------
// | 解题方式
// +--------------------------------------------------------------
class FibonacciQuery
{
/**
* FibonacciQuery constructor.
*
* @param array $container
* @param $num
*/
public function __construct(array $container, $num)
{
$count = count($container);
$lower = $key = $result = 0;
$high = $count - 1;
//计算$count位于斐波那契数列的位置
while ($count > ($this->produced($key) - 1)) {
$key++;
}
//将不满的数值补全,补的数值为数组的最后一位
for ($j = $count; $j < $this->produced($key) - 1; $j++) {
$container[$j] = $container[$count - 1];
}
//查找开始
while ($lower <= $high) {
//计算当前分隔的下标
$mid = $lower + $this->produced($key - 1) - 1;
if ($num < $container[$mid]) {
$high = $mid - 1;
$key -= 1; //斐波那契数列数列下标减一位
} else if ($num > $container[$mid]) {
$lower = $mid + 1;
$key -= 2; //斐波那契数列数列下标减两位
}
if ($mid <= $count - 1) {
$result = $mid;
break;
} else { //这里$mid大于$count-1说明是补全数值,返回$count-1
$result = $count - 1;
break;
}
}
var_dump($result);
}
/**
* 创建一个生产斐波那契数列
*
* @param $length
* @return int
*/
public function produced($length)
{
if ($length < 2) {
return ($length == 0 ? 0 : 1);
}
return $this->produced($length - 1) + $this->produced($length - 2);
}
}
// +---------------------------------------------------------
// | 方案测试
// +---------------------------------------------------------
new FibonacciQuery([4, 5, 7, 8, 9, 10, 8], 8);
广度优先查找
/**
* 广度优先搜索
*
* -------------------------------------------------------------
* 思路分析: BFS并不使用经验法则算法。从算法的观点,所有因为展开节点而得到的子节点都会被加进一个先进先出的队列中
* 时间复杂度:O(n)
* -------------------------------------------------------------
* 宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。
* Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。
* 其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。
* 换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
*/
class BFSQuery
{
/**
* @var array 关系网络
*/
protected $relationship;
/**
* @var SplQueue 处理队列
*/
protected $queue;
/**
* @var string 搜索结果
*/
protected $target;
/**
* BFSQuery constructor.
*
* @param array $relationship
* @param string $target
*/
public function __construct(array $relationship, $target)
{
$this->relationship = $relationship;
$this->queue = new SplQueue();
$this->target = $target;
$this->generator($this->relationship);
}
/**
* 开始入列
*
* @param array $relation
* @return Generator
*/
public function generator($relation)
{
foreach ($relation as $value) {
$this->schedule($value);
}
}
/**
* 队列入队
*
* @param $item
* @return int
*/
public function schedule($item)
{
$this->queue->enqueue($item);
}
/**
* 队列中查找符合条件
*
* @return string
*/
public function run()
{
$result = $this->target . '没有人有~!';
while (!$this->queue->isEmpty()) {
// 出队列
$item = $this->queue->dequeue();
if (!isset($item['friend'])) {
continue;
}
if (!isset($item['fruit'])) {
continue;
}
$totalFruit = count($item['fruit']);
$mark = 0;
while ($totalFruit > $mark) {
if ($item['fruit'][$mark] === $this->target) {
$result = '找到了~!';
break 2;
}
$mark++;
}
$this->generator($item['friend']);
}
return $result;
}
}
// +---------------------------------------------------------------
// | 方案测试
// +---------------------------------------------------------------
// 你现在需要一个 `mango` ,所以你需要在你的朋友圈里搜刮,你可以先从Jack 与 tom 身上找,
// 然后再从他们的朋友身上找,然后再从他们朋友的朋友哪里找
$me = array (
'jack' => array (
'fruit' => array ('apple', 'banana', 'dragon'),
'friend' => array (
'lucy' => array (
'fruit' => array ('bear', 'watermelon'),
'friend' => array (
'marco' => array (
'fruit' => array ('mango', 'cherry'), // Mango 在这儿
'friend' => array (
'...',
)
),
),
),
'bob' => array (
'fruit' => array ('orange', 'mangosteen', 'peach'),
'friend' => array (
'',
),
),
),
),
'tom' => array (
'fruit' => array (
'apple',
'banana',
),
'friend' => array (
'lucy' => array (
'fruit' => array (),
'friend' => array (
'lucy' => array (
'fruit' => array ('bear', 'watermelon'),
'friend' => array (
'marco' => array (
'fruit' => array ('mango', 'cherry'), // Mango 在这儿也有
'friend' => array (
'...',
)
),
),
),
),
),
'bob' => array (
'fruit' => array ('apple', 'peach'),
'friend' => array (
'marco' => array (
'fruit' => array ('mango', 'cherry'), // Mango 在这儿也有
'friend' => array (
'Marco 有无数多的盆友...',
)
),
)
),
),
)
);
echo (new BFSQuery($me, 'mango'))->run();
KMP算法
<?php
/**
* KMP算法
*
* -------------------------------------------------------------
* KMP算法是一种改进的字符串匹配算法
* KMP精要:KMP在进行朴素匹配时,如果发现不匹配字符时,通过对已经匹配的那部分字符串的最大前缀来快速找到下一个模式串需要匹配的位置。
* KMP对模式进行预处理时间复杂度O(m),匹配时间复杂度O(n),总的KMP时间复杂度为O(m+n)。
* 参考 字符串匹配的KMP算法 — 阮一峰
*/
// +------------------------------------------------------------
// | 解题方式 | 这儿,可能有用的解决方案
// +------------------------------------------------------------
class KMP
{
public $haystack;
public $needle;
private $_haystackLen;
private $_needleLen;
private $_matchTable;
private $_isMatch;
//构造函数
function __construct($haystack, $needle)
{
$this->haystack = $haystack;
$this->needle = $needle;
//初始化一些参数
$this->_haystackLen = $this->getLen($this->haystack);
$this->_needleLen = $this->getLen($this->needle);
$this->_matchTable = $this->getMatchTable();
$this->_isMatch = false;
}
//类似strpos函数功能
public function strpos()
{
//haystack
$haystackIdx = $matchNum = 0;
while ($haystackIdx <= $this->_haystackLen - $this->_needleLen) {
//needle
$needIdx = 0;
for (; $needIdx < $this->_needleLen; $needIdx++) {
if (strcmp($this->haystack[$haystackIdx], $this->needle[$needIdx]) <> 0) {
if ($matchNum > 0) {
$lastMatchValue = $this->getLastMatchValue($needIdx - 1);
$haystackIdx += $this->getStep($matchNum, $lastMatchValue);
$matchNum = 0;
} else {
$haystackIdx++;
}
break;
} else {
$haystackIdx++;
$matchNum++;
if ($matchNum == $this->_needleLen) {
$this->_isMatch = true;
break;
}
}
}
if ($this->_isMatch == true) {
break;
}
}
return $this->_isMatch ? $haystackIdx - $this->_needleLen : false;
}
//获取字符长度
private function getLen($str)
{
return mb_strlen($str, 'utf-8');
}
//获取部分匹配表
private function getMatchTable()
{
$matchTable = [];
for ($i = 0; $i < $this->_needleLen; $i++) {
$intersectLen = 0;
$nowStr = mb_substr($this->needle, 0, $i + 1, 'utf-8');
$preFixArr = $this->getPreFix($nowStr);
$sufFixArr = $this->getSufFix($nowStr);
if ($preFixArr && $sufFixArr) {
$intersectArr = array_intersect($preFixArr, $sufFixArr);
if (!empty($intersectArr)) {
$intersect = array_pop($intersectArr);
$intersectLen = mb_strlen($intersect, 'utf-8');
}
}
$matchTable[$i] = $intersectLen;
}
return $matchTable;
}
//获取前缀数组
private function getPreFix($str)
{
$outArr = [];
$strLen = $this->getLen($str);
if ($strLen > 1) {
for ($i = 1; $i < $strLen; $i++) {
$outArr[] = mb_substr($str, 0, $i, 'utf-8');
}
}
return $outArr;
}
//获取后缀数组
private function getSufFix($str)
{
$outArr = [];
$strLen = $this->getLen($str);
if ($strLen > 1) {
for ($i = 1; $i < $strLen; $i++) {
$outArr[] = mb_substr($str, $i, null, 'utf-8');
}
}
return $outArr;
}
//计算步长
private function getStep($matchNum, $lastMatchValue)
{
return $matchNum - $lastMatchValue;
}
//获取最后匹配值
private function getLastMatchValue($index)
{
return isset($this->_matchTable[$index]) ? $this->_matchTable[$index] : 0;
}
}
// +------------------------------------------------------------------
// | 方案测试 | php `this.php` || PHPStorm -> 右键 -> Run `this.php`
// +------------------------------------------------------------------
$str = 'a b a c a a b a c a b a c a b a a b b';
$subStr = 'a b a c a b';
$kmp = new KMP($str, $subStr);
var_dump($kmp->strpos());
$kmp->haystack = 'pull requests';
$kmp->needle = 'sts';
var_dump($kmp->strpos());
$kmp->haystack = 'i love you';
$kmp->needle = 'hate';
var_dump($kmp->strpos());
迪克斯特拉算法
<?php
/**
* 迪克斯特拉算法
*
* -------------------------------------------------------------
* 思路分析:单源最短路径问题
* -------------------------------------------------------------
* Dijkstra 算法一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表的方式,
* 这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。
* 因此,在包含负边全的图中要找出最短路径,可以使用另一种算法 -- 贝克曼-福德算法
*/
// +------------------------------------------------------------
// | 解题方式
// +------------------------------------------------------------
/**
* DijkstraQuery
*
* @uses PHPStorm
* @version 1.0
* @author Pu ShaoWei <pushaowei0727@gmail.com>
*/
class DijkstraQuery
{
/**
* @var array
*/
protected $graph;
/**
* @var array
*/
protected $processed;
/**
* @var int
*/
protected $infinity;
/**
* @var string
*/
protected $start;
/**
* @var string
*/
protected $end;
/**
* DijkstraQuery constructor.
*
* @param array $graph
* @param $start
* @param $end
*/
public function __construct(array $graph, $start, $end)
{
$this->graph = $graph;
$this->start = $start;
$this->end = $end;
$this->processed = array ();
$this->infinity = mt_getrandmax();
}
/**
* 最短路径
*
* @return string
*/
public function calculate()
{
$costs = $this->graph[$this->start];
$costs[$this->end] = $this->infinity;
$node = $this->findLowestCostNode($costs);
while (null !== $node) {
$cost = $costs[$node];
$neighbors = $this->graph[$node] ?? array ();
foreach ($neighbors as $neighbor => $distance) {
$newCost = $cost + $distance;
if ($costs[$neighbor] > $newCost) {
$costs[$neighbor] = $newCost;
}
}
array_push($this->processed, $node);
$node = $this->findLowestCostNode($costs);
}
return 'The shortest distance for:' . $costs[$this->end];
}
/**
* findLowestCostNode
*
* @param $costs
* @return null
*/
protected function findLowestCostNode($costs)
{
$lowestCost = $this->infinity;
$lowestCostNode = null;
foreach ($costs as $node => $cost) {
if ($cost < $lowestCost && !in_array($node, $this->processed)) {
$lowestCost = $cost;
$lowestCostNode = $node;
}
}
return $lowestCostNode;
}
}
// +-----------------------------------------------------------
// | 验证 me --> claire
// +-----------------------------------------------------------
// ∞
$graph = array (
'me' => array (
'alice' => 6,
'bob' => 2,
),
'alice' => array (
'claire' => 1,
),
'bob' => array (
'alice' => 3,
'claire' => 5,
),
'claire' => array (// 没有任何邻居
),
);
echo (new DijkstraQuery($graph, 'me', 'claire'))->calculate();