<?php
/**
* PS:关于预定义接口Iterator, ArrayAccess, Countable的文章已经介绍过了,不认识的可以往前翻翻
*/
class SplDoublyLinkedList implements Iterator, ArrayAccess, Countable
{
/**
* @var _llist 定义一个数组用于存放数据
*/
protected $_llist = array();
/**
* @var _it_mode 链表的迭代模式
*/
protected $_it_mode = 0;
/**
* @var _it_pos 链表指针
*/
protected $_it_pos = 0;
/**
* 迭代模式
* @see setIteratorMode
*/
const IT_MODE_LIFO = 0x00000002;
const IT_MODE_FIFO = 0x00000000;
const IT_MODE_KEEP = 0x00000000;
const IT_MODE_DELETE = 0x00000001;
/**
* @return 返回被移出尾部节点元素
* @throw RuntimeException 如果链表为空则抛出异常
*/
public function pop()
{
if (count($this->_llist) == 0) {
throw new RuntimeException("Can't pop from an empty datastructure");
}
return array_pop($this->_llist);
}
/**
* @return 返回被移出头部节点元素
* @throw RuntimeException 如果链表为空则抛出异常
*/
public function shift()
{
if (count($this->_llist) == 0) {
throw new RuntimeException("Can't shift from an empty datastructure");
}
return array_shift($this->_llist);
}
/**
* 往链表尾部添加一个节点元素
* @param $data 要添加的节点元素
*/
public function push($data)
{
array_push($this->_llist, $data);
return true;
}
/**
* 往链表头部添加一个节点元素
* @param $data 要添加的节点元素
*/
public function unshift($data)
{
array_unshift($this->_llist, $data);
return true;
}
/**
* @return 返回尾部节点元素,并把指针指向尾部节点元素
*/
public function top()
{
return end($this->_llist);
}
/**
* @return 返回头部节点元素,并把指针指向头部节点元素
*/
public function bottom()
{
return reset($this->_llist);
}
/**
* @return 返回链表节点数
*/
public function count()
{
return count($this->_llist);
}
/**
* @return 判断链表是否为空
*/
public function isEmpty()
{
return ($this->count() == 0);
}
/**
* 设置迭代模式
* - 迭代的顺序 (先进先出、后进先出)
* - SplDoublyLnkedList::IT_MODE_LIFO (堆栈)
* - SplDoublyLnkedList::IT_MODE_FIFO (队列)
*
* - 迭代过程中迭代器的行为
* - SplDoublyLnkedList::IT_MODE_DELETE (删除已迭代的节点元素)
* - SplDoublyLnkedList::IT_MODE_KEEP (保留已迭代的节点元素)
*
* 默认的模式是 0 : SplDoublyLnkedList::IT_MODE_FIFO | SplDoublyLnkedList::IT_MODE_KEEP
*
* @param $mode 新的迭代模式
*/
public function setIteratorMode($mode)
{
$this->_it_mode = $mode;
}
/**
* @return 返回当前的迭代模式
* @see setIteratorMode
*/
public function getIteratorMode()
{
return $this->_it_mode;
}
/**
* 重置节点指针
*/
public function rewind()
{
if ($this->_it_mode & self::IT_MODE_LIFO) {
$this->_it_pos = count($this->_llist)-1;
} else {
$this->_it_pos = 0;
}
}
/**
* @return 判断指针对应的节点元素是否存在
*/
public function valid()
{
return array_key_exists($this->_it_pos, $this->_llist);
}
/**
* @return 返回当前指针的偏移位置
*/
public function key()
{
return $this->_it_pos;
}
/**
* @return 返回当前指针对应的节点元素
*/
public function current()
{
return $this->_llist[$this->_it_pos];
}
/**
* 将指针向前移动一个偏移位置
*/
public function next()
{
if ($this->_it_mode & self::IT_MODE_LIFO) {
if ($this->_it_mode & self::IT_MODE_DELETE) {
$this->pop();
}
$this->_it_pos--;
} else {
if ($this->_it_mode & self::IT_MODE_DELETE) {
$this->shift();
} else {
$this->_it_pos++;
}
}
}
/**
* @return 偏移位置是否存在
*
* @param $offset 偏移位置
* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常
*/
public function offsetExists($offset)
{
if (!is_numeric($offset)) {
throw new OutOfRangeException("Offset invalid or out of range");
} else {
return array_key_exists($offset, $this->_llist);
}
}
/**
* @return 获取偏移位置对应的值
*
* @param $offset 偏移位置
* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常
*/
public function offsetGet($offset)
{
if ($this->_it_mode & self::IT_MODE_LIFO) {
$realOffset = count($this->_llist)-$offset;
} else {
$realOffset = $offset;
}
if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) {
throw new OutOfRangeException("Offset invalid or out of range");
} else {
return $this->_llist[$realOffset];
}
}
/**
* @return 设置偏移位置对应的值
*
* @param $offset 偏移位置
* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常
*/
public function offsetSet($offset, $value)
{
if ($offset === null) {
return $this->push($value);
}
if ($this->_it_mode & self::IT_MODE_LIFO) {
$realOffset = count($this->_llist)-$offset;
} else {
$realOffset = $offset;
}
if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) {
throw new OutOfRangeException("Offset invalid or out of range");
} else {
$this->_llist[$realOffset] = $value;
}
}
/**
* @return 删除偏移位置对应的值
*
* @param $offset 偏移位置
* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常
*/
public function offsetUnset($offset)
{
if ($this->_it_mode & self::IT_MODE_LIFO) {
$realOffset = count($this->_llist)-$offset;
} else {
$realOffset = $offset;
}
if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) {
throw new OutOfRangeException("Offset invalid or out of range");
} else {
array_splice($this->_llist, $realOffset, 1);
}
}
}
?>
示例:
<?php
/**
* Spl php标准库
* 双向链
* @author Yel
* @date 2021-4-14
*/
//创建一个 SplDoulyLinkedList 对象
$SplDoulyLinkedList = new SplDoublyLinkedList();
//查看对象内容
var_dump( $SplDoulyLinkedList);
//输出
// object(SplDoublyLinkedList)#1 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(0) {
// }
// }
// 向对象中添加 A B 两个 数据 add: (PHP 5 >= 5.5.0, PHP 7)
$SplDoulyLinkedList->add(0,'A'); //在指定的索引处添加/插入新值 超出范围报错 默认从 0 开始
$SplDoulyLinkedList->add(1,'B');
$SplDoulyLinkedList->add(2,'C');
//查看对象内容
var_dump( $SplDoulyLinkedList);
//输出
// object(SplDoublyLinkedList)#1 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(3) {
// [0]=>
// string(1) "A"
// [1]=>
// string(1) "B"
// [2]=>
// string(1) "C"
// }
// }
//遍历该对象
foreach ($SplDoulyLinkedList as $key => $value) {
var_dump("$key => $value");
}
//输出
// string(7) "0 => A"
// string(7) "1 => B"
// string(7) "2 => C"
//查看
echo "<br>" . $SplDoulyLinkedList->bottom(); //查看 SplDoulyLinkedList 对象 开头节点的值 只是查看 输出 A
echo "<br>" . $SplDoulyLinkedList->top(); //查看 SplDoulyLinkedList 对象 最末节点的值 只是查看 输出 C
echo "<br>" . $SplDoulyLinkedList->count(); //查看 SplDoulyLinkedList 对象 当前元素数量 只是查看 输出 3
echo "<br>current:" . $SplDoulyLinkedList->current();//查看 SplDoulyLinkedList 对象 当前节点元素 输出NULL 必须使用 $SplDoulyLinkedList->rewind()把迭代器放到起点
echo "<br>" . $SplDoulyLinkedList->rewind(); // 将 SplDoulyLinkedList 对象迭代器倒回开始
echo "<br>" . $SplDoulyLinkedList->current();// 输出 A
echo "<br>" . $SplDoulyLinkedList->next(); // 将 节点移向下一个元素 无输出
echo "<br>" . $SplDoulyLinkedList->current();// 输出 B
echo "<br>" . $SplDoulyLinkedList->rewind(); // 重置节点
echo "<br>" . $SplDoulyLinkedList->current();// 输出 A
echo "<br>" . $SplDoulyLinkedList->next(); // 将 节点移向下一个元素 无输出
echo "<br>" . $SplDoulyLinkedList->prev(); // 将 节点移向上一个元素 无输出
echo "<br>" . $SplDoulyLinkedList->current();// 输出 A
echo "<br>" . $SplDoulyLinkedList->key();// 输出SplDoulyLinkedList 对象 当前节点 输出 0
//检测
echo "<br>" ;
var_dump($SplDoulyLinkedList->isEmpty()); //检测 SplDoulyLinkedList 对象 是否为空 输出 false
var_dump($SplDoulyLinkedList->valid()); //检测 SplDoulyLinkedList 对象 当前节点是否有效 输出 true
//输出 SplDoulyLinkedList对象所有元素
echo '<br>输出 所有元素 :';
$SplDoulyLinkedList->rewind(); //先把节点调回第一位
while(1){
//检测为空 或者 节点无效时退出
if($SplDoulyLinkedList->isEmpty() || !$SplDoulyLinkedList->valid()){
break;
}
//输出当前节点 与 对应 元素
echo "<br>" . $SplDoulyLinkedList->key() . " => " . $SplDoulyLinkedList->current() ;
$SplDoulyLinkedList->next();
}
// 输出 所有元素 :
// 0 => A
// 1 => B
// 2 => C
//查看对象内容
echo "<br>" ;
var_dump( $SplDoulyLinkedList);
//输出
// object(SplDoublyLinkedList)#1 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(3) {
// [0]=>
// string(1) "A"
// [1]=>
// string(1) "B"
// [2]=>
// string(1) "C"
// }
// }
// 在对象中弹出一个元素
echo "<br>" . $SplDoulyLinkedList->shift(); //从双链表的开头移出一个节点 输出 A
echo "<br>" . $SplDoulyLinkedList->pop(); //从双链表的末尾移出一个节点 输出 C
echo "<br>" . $SplDoulyLinkedList->unshift("D"); //从双链表的开头插入一个节点 输出 true
echo "<br>" . $SplDoulyLinkedList->unshift("E"); //从双链表的开头插入一个节点 输出 true
echo "<br>" . $SplDoulyLinkedList->push("F"); //从双链表的末尾添加一个节点 输出 true
echo "<br>" . $SplDoulyLinkedList->push("G"); //从双链表的末尾添加一个节点 输出 true
//查看对象内容
echo "<br>" ;
var_dump( $SplDoulyLinkedList);
//输出
// object(SplDoublyLinkedList)#1 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(5) {
// [0]=>
// string(1) "E"
// [1]=>
// string(1) "D"
// [2]=>
// string(1) "B"
// [3]=>
// string(1) "F"
// [4]=>
// string(1) "G"
// }
// }
//自带 序列化 与反序列化
//序列化
$SplDoulyLinkedList_serialize = $SplDoulyLinkedList->serialize();
var_dump($SplDoulyLinkedList_serialize); //string(49) "i:0;:s:1:"E";:s:1:"D";:s:1:"B";:s:1:"F";:s:1:"G";"
//反序列化
$List = new SplDoublyLinkedList();
$List->unserialize($SplDoulyLinkedList_serialize);
var_dump($List);
//输出
// object(SplDoublyLinkedList)#2 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(5) {
// [0]=>
// string(1) "E"
// [1]=>
// string(1) "D"
// [2]=>
// string(1) "B"
// [3]=>
// string(1) "F"
// [4]=>
// string(1) "G"
// }
// }
echo "<br>" ;
echo "<br>" ;
// 序列化函数
$serialize = serialize($SplDoulyLinkedList);
var_dump($serialize); //string(81) "C:19:"SplDoublyLinkedList":49:{i:0;:s:1:"E";:s:1:"D";:s:1:"B";:s:1:"F";:s:1:"G";}"
$List = unserialize($serialize);
var_dump($List);
//输出
// object(SplDoublyLinkedList)#3 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(5) {
// [0]=>
// string(1) "E"
// [1]=>
// string(1) "D"
// [2]=>
// string(1) "B"
// [3]=>
// string(1) "F"
// [4]=>
// string(1) "G"
// }
// }
echo "<br>" ;
echo "<br>" ;
echo "<br>" . $List->offsetGet(2); // 获取 指定节点的元素
echo "<br>" . $List->offsetSet(2, "Z"); // 将指定节点 的元素 替换 位 特定值 无输出
echo "<br>" . $List->offsetSet(3, "Z"); // 将指定节点 的元素 替换 位 特定值 无输出
var_dump($List);
//输出
// object(SplDoublyLinkedList)#3 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(5) {
// [0]=>
// string(1) "E"
// [1]=>
// string(1) "D"
// [2]=>
// string(1) "Z"
// [3]=>
// string(1) "Z"
// [4]=>
// string(1) "G"
// }
// }
echo "<br>" . $List->offsetUnset(1); //删除指定节点元素 ,并且重新制定节点键值
echo "<br>" ;
var_dump($List);
//输出
// object(SplDoublyLinkedList)#3 (2) {
// ["flags":"SplDoublyLinkedList":private]=>
// int(0)
// ["dllist":"SplDoublyLinkedList":private]=>
// array(4) {
// [0]=>
// string(1) "E"
// [1]=>
// string(1) "Z"
// [2]=>
// string(1) "Z"
// [3]=>
// string(1) "G"
// }
// }
//(PHP 5> = 5.3.0,PHP 7)
echo "<br>" . $List->offsetExists(7); // 检测指定 节点是否存在 输出 false
echo "<br>" ;
var_dump($List);