• 狄克斯特拉(Dijkstra) 算法 php实现


    算法图解》中提到的狄克斯特拉算法,用php实现。

    一 原理及解释

    根据示例图求出起点到终点的最小耗费路径。

    因为涉及每条路径的权重,所以这种算法仅适合有向路径。

    所谓有向路径,指仅从起点指向终点的路径。

    相对的无向路径,指起点和终点互相指向的路径,一般这样的路径不带箭头。

    该算法设定每条路径没有权重为负的路径,且没有不可指向终点的路径,所以所有节点都有效。

    起点“O”,终点“C”,实例图如下

    根据图示,路径0到A耗费1总耗费1记作A<-O:1:1,以此类推B<-O:2:1,选取耗费最小的路径A<-O:1:1,此时O节点遍历完成。

    从节点A再次开始C->A:4,总耗费需要加上之前路径的耗费所以C->A:4:5,A节点遍历完成。

    剩余B节点未处理,选取B节点开始C->B:1:3,B节点遍历完成。因为总消耗3小于总消耗5,所以选择C->B路径。

    因为C节点是终点,不用便利。所以其余节点遍历完成,则C节点处理完成。

    最终选择路径O->B->C,总消耗3。

    二 处理流程

    1、初始化

    节点总耗费父节点遍历完成
    OFALSEFALSEFALSE
    AFALSEFALSEFALSE
    BFALSEFALSEFALSE
    CFALSEFALSEFALSE

    2、选取起点节点O

    节点总耗费父节点遍历完成
    OFALSEFALSETRUE
    A1OFALSE
    B2OFALSE
    CFALSEFALSEFALSE

    下一步,选取耗费最少且未遍历完成的节点,但是要注意耗费的值不能是FALSE。可将耗费中FALSE视为最大。

    某些语言中有无穷大的常量关键字,可以直接用大小对比排除未设置耗费数量的节点。

    3、选取节点A

    节点总耗费父节点遍历完成
    OFALSEFALSETRUE
    A1OTRUE
    B2OFALSE
    C4AFALSE

    B->A:3:4,原有节点总耗费为2,小于4。所以不采用B->A,即B->A的数据不更新到表中。

    下一步,选取耗费最少且未遍历完成的节点

    4、选取节点B

    节点总耗费父节点遍历完成
    OFALSEFALSETRUE
    A1OTRUE
    B2OTRUE
    C3BTRUE

    B->C:1:3,原有节点总耗费为4,大于3。所以采用C->B,即C->B的数据更新到表中。

    此时除去终点的节点都处理完,代表终点处理完成,即节点C处理完成。

    5、处理流程总结

    1、初始化表

    2、选取起点节点

    3、开始首次处理,即是用节点数据更新表数据

    4、设置节点处理完成

    5、选取耗费最少且未遍历完成的节点

    6、重复4、5步操作。当节点为终点节点时推出循环

    7、生成结果

    三 php实现

    1. //狄克斯特拉(Dijkstra) 算法 实现
    2. class Test
    3. {
    4. /**
    5. * $tableitem =['node'=>'','cost'=>'','prev'=>'']
    6. */
    7. private $table = []; //计算开销数据
    8. private $result = []; //结算表
    9. private $data = [];
    10. private $start;
    11. private $end;
    12. public function __construct($data, $start, $end)
    13. {
    14. $this->data = $data;
    15. $this->start = $start;
    16. $this->end = $end;
    17. }
    18. private function insert_tables($data)
    19. {
    20. foreach ($data as $key => $value) {
    21. $this->insert_table($key);
    22. if (is_array($value)) {
    23. $this->insert_tables($value);
    24. }
    25. }
    26. }
    27. /**
    28. * 向table加数据
    29. *
    30. * @param [type] $node
    31. * @return void
    32. * @author wj
    33. * @date 2023-10-18
    34. */
    35. private function insert_table($node)
    36. {
    37. if (!isset($this->table[$node])) {
    38. $this->table[$node] = [
    39. 'node' => $node,
    40. 'cost' => false,
    41. 'prev' => false,
    42. 'isChecked' => false,
    43. ];
    44. }
    45. }
    46. /**
    47. * 改table数据
    48. *
    49. * @param [type] $node
    50. * @param [type] $cost
    51. * @param [type] $prev
    52. * @return void
    53. * @author wj
    54. * @date 2023-10-18
    55. */
    56. private function update_table($node, $cost, $prev)
    57. {
    58. $info = $this->table[$node];
    59. if ($cost < $info['cost'] || false == $info['cost']) {
    60. $this->table[$node]['cost'] = $cost;
    61. $this->table[$node]['prev'] = $prev;
    62. }
    63. }
    64. /**
    65. * 处理节点
    66. *
    67. * @param boolean $node
    68. * @return void
    69. * @author wj
    70. * @date 2023-10-18
    71. */
    72. private function deal_node($node = false)
    73. {
    74. if (empty($node)) {
    75. $node = $this->get_need_deal_node();
    76. }
    77. if (empty($node) || !isset($this->data[$node])) {
    78. return false;
    79. }
    80. $usedata = $this->data[$node];
    81. $prev = $node;
    82. $table_cost = $this->table[$prev]['cost'];
    83. foreach ($usedata as $key => $cost) {
    84. $totalcost = $cost + (int) $table_cost;
    85. $this->update_table($key, $totalcost, $prev);
    86. }
    87. $this->table[$node]['isChecked'] = true;
    88. return true;
    89. }
    90. /**
    91. * 取得最小未处理节点
    92. *
    93. * @param [type] $node
    94. * @return void
    95. * @author wj
    96. * @date 2023-10-18
    97. */
    98. private function get_need_deal_node()
    99. {
    100. $table = $this->table;
    101. $min_node = false;
    102. $cost = false;
    103. foreach ($table as $key => $value) {
    104. if ($value['isChecked'] || false === $value['cost']) {
    105. continue;
    106. }
    107. if ($value['cost'] < $cost || false === $cost) {
    108. $cost = $value['cost']; //最小开销
    109. $min_node = $key;
    110. }
    111. }
    112. return $min_node;
    113. }
    114. /**
    115. * 设定data 无没用分支 没有负权
    116. *
    117. * @param [type] $data
    118. * @param [type] $start
    119. * @param [type] $end
    120. * @return void
    121. * @author wj
    122. * @date 2023-10-18
    123. */
    124. public function dotest()
    125. {
    126. //初始化table;
    127. $this->insert_tables($this->data);
    128. $node = $this->start;
    129. //处理节点
    130. while ($node) {
    131. $result = $this->deal_node($node);
    132. $node = $this->get_need_deal_node();
    133. if ($node == $this->end) {
    134. $this->table[$this->end]['isChecked'] = true;
    135. break;
    136. }
    137. }
    138. }
    139. public function getresult()
    140. {
    141. $end = $this->end;
    142. $result = [];
    143. $node = $end;
    144. while ($node) {
    145. $nodeinfo = $this->table[$node];
    146. $result[$nodeinfo['node']] = $nodeinfo;
    147. $node = $nodeinfo['prev'];
    148. }
    149. $result = array_reverse($result);
    150. $path = implode("->", array_keys($result));
    151. $lastnode = end($result);
    152. $cost = $lastnode['cost'];
    153. $data = [
    154. 'result' => $result,
    155. 'path' => $path,
    156. 'cost' => $cost,
    157. ];
    158. return $data;
    159. }
    160. }
    161. $data = [
    162. "O" => [
    163. "A" => 0,
    164. "B" => 1,
    165. ],
    166. "A" => [
    167. "C" => 1,
    168. "B" => 2,
    169. ],
    170. "B" => [
    171. "D" => 2,
    172. ],
    173. "C" => [
    174. "D" => 3,
    175. ],
    176. ];
    177. $t = new Test($data, "O", "D");
    178. $t->dotest();
    179. $data = $t->getresult();
    180. var_dump($data);
    1. #执行结果
    2. array(3) {
    3. 'result' =>
    4. array(3) {
    5. 'O' =>
    6. array(4) {
    7. 'node' =>
    8. string(1) "O"
    9. 'cost' =>
    10. bool(false)
    11. 'prev' =>
    12. bool(false)
    13. 'isChecked' =>
    14. bool(true)
    15. }
    16. 'B' =>
    17. array(4) {
    18. 'node' =>
    19. string(1) "B"
    20. 'cost' =>
    21. int(1)
    22. 'prev' =>
    23. string(1) "O"
    24. 'isChecked' =>
    25. bool(true)
    26. }
    27. 'D' =>
    28. array(4) {
    29. 'node' =>
    30. string(1) "D"
    31. 'cost' =>
    32. int(3)
    33. 'prev' =>
    34. string(1) "B"
    35. 'isChecked' =>
    36. bool(true)
    37. }
    38. }
    39. 'path' =>
    40. string(7) "O->B->D"
    41. 'cost' =>
    42. int(3)
    43. }

  • 相关阅读:
    正确重写equals和hashcode方法
    webpack + vite 前端构建工具
    后代选择器(非常重要)
    AC8015笔记
    房贷结清后抵押注销及个税App专项扣除费修改
    Linux 错误处理(字符设备基础三)
    MySQL InnoDB数据存储结构
    完成flex布局与float布局
    discuzx3.4帖子表pre_forum_post中invisible字段说明
    服务器如何防御攻击,有哪些方法
  • 原文地址:https://blog.csdn.net/lsswear/article/details/133922641