<html>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
</head>
<body>
<h1>约瑟夫问题解决</h1>
<?php
class Child
{
public $no;
public $next = null;
public function __construct($no=''){
$this->no = $no;
}
}
// 定义一个指向第一个小朋友的引用
$first = null;
$n = 4; // 表示有几个小朋友
// 写一个函数来创建4个小朋友的环形链表
// 深入分析下$first参数为什么加地址符&
// 作用:把n个小孩构建出一个环形链表
// $first变量指向了第一个小朋友
function addChild(&$first,$n){
// 1.头结点不能动
$cur = null;
for ($i = 0; $i < $n; $i++)
{
$child = new Child($i+1);
// 怎么构成一个环形链表
if ($i==0)
{
$first = $child;
$first->next = $child;
$cur = $first;
}else
{
$cur->next = $child;
$child->next = $first;
$cur = $cur->next;
}
}
}
// 显示环形链表中的所有小朋友
// 必须要把头给出来
function showChild($first){
// 遍历
$cur = $first;
while($cur->next!=$first){
// 显示
echo '小孩的编号是'.$cur->no.'<br />';
$cur = $cur->next;
}
// 当退出while循环时,已经到了环形链表的最后
// 所以还要处理下最后这个小孩节点
echo '小孩的编号是'.$cur->no.'<br />';
}
$m = 3; // 数3下
$k = 2; // 从第2个开始数
// 问题简化,从第一个小孩开始数,数2
// 看看出圈的顺序
function countChild($first,$m,$k){
if ($k>$m)
{
exit('数据设定不对');
}
// 思考:找到一个小孩,就要把他从环形链表踢出去
// 为了能够删除某个小孩,需要一个辅助变量
// 该变量指向的小孩在$first前面
$tail = $first;
while($tail->next!=$first){
$tail = $tail->next;
}
// 考虑是从第几个人开始数数
for ($i = 0; $i < $k-1; $i++)
{
$tail = $tail->next;
$first = $first->next;
}
// 当退出while循环时候 $tail就指向了最后这个小孩
// 让$first和$tail向后移动
// 每移动1次,相当于数了2下
// 移动2次,相当于数了3下
// 因为自己数的时候是不需要动的
// 当$tail==$first说明只有一个人
while($tail!=$first){
for ($i = 0; $i < $m-1; $i++)
{
$tail = $tail->next;
$first = $first->next;
}
// 打印出出列小孩编号
echo '出圈人的编号是'.$first->no.'<br />';
// 把$first指向的节点小孩删除环形链表
$first = $first->next;
$tail->next = $first;
}
echo '最后留在圈圈的人的编号是'.$tail->no;
}
addChild($first,4);
showChild($first);
// 真正的来玩儿游戏
if ($m<$n)
{
countChild($first,$m,$k);
}
?>
</body>
</html>