雙鏈表對PHP開發程式來講是很重要的一種資料結構,可以把PHP數組中想想成一個雙鏈表,而PHP內建的SplDoublyLinkedList類通過實現迭代器、數組訪問和擷取數量的介面使程式訪問對象變得訪問數組一樣方便。
SplDoublyLinkedList類代碼如下:
<?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$doubly=new SplDoublyLinkedList();$doubly->push('a');$doubly->push('b');$doubly->push('c');$doubly->push('d');echo '初始鏈表結構:';var_dump($doubly);echo '<br/> 先進先出Keep模式迭代輸出: <br/>';$doubly->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_KEEP);$doubly->rewind();foreach($doubly as $key=>$value){ echo $key.' '.$value."<br/>";}echo '<br/>後進先出Keep模式迭代輸出:<br/>';$doubly->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP);$doubly->rewind();foreach($doubly as $key=>$value){ echo $key.' '.$value."<br/>";}echo '<br/>後進先出Delete模式迭代輸出:<br/>';$doubly->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_DELETE);$doubly->rewind();foreach($doubly as $key=>$value){ if($key == 1) break; echo $key.' '.$value."<br/>";}echo '<br/>Delete模式迭代之後的鏈表:';var_dump($doubly);?>
輸出:
初始鏈表結構:
object(SplDoublyLinkedList)[1] private 'flags' => 0 private 'dllist' => array (size=4) 0 => 'a' (length=1) 1 => 'b' (length=1) 2 => 'c' (length=1) 3 => 'd' (length=1)
先進先出Keep模式迭代輸出:
0 a
1 b
2 c
3 d
後進先出Keep模式迭代輸出:
3 d
2 c
1 b
0 a
後進先出Delete模式迭代輸出:
3 d
2 c
Delete模式迭代之後的鏈表:
object(SplDoublyLinkedList)[1] private 'flags' => 3 private 'dllist' => array (size=2) 0 => 'a' (length=1) 1 => 'b' (length=1)
最後一個問題:SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_KEEP 模式下迭代輸出什嗎?
相關推薦:
PHP雙向鏈表基礎詳解
JavaScript雙向鏈表定義與使用方法
PHP實現雙向鏈表的一例代碼