PHP 算法 数据结构


算法 数据结构


正文

数据结构

线性表 顺序存储

线性表是⼀个有限序列,表中各个元素是相继排列的,且每两个相邻元素之间都有直接前驱和直接后继的逻辑关系。

线性表有 顺序表、 单链表、 循环链表、 双向链表 等。

定义:线性表(Linear List) :是由n(n≧0)个数据元素(结点) [a1,a2, …an] 组成的有限序列。数据元素是一个抽象的符号,其具体含义在不同的情况下一般不同。 该序列中的所有结点具有相同的数据类型。其中数据元素的个数n称为线性表的长度。 当n=0时,称为空表。 当n>0时,将非空的线性表记作: (a1,a2,…an) a1称为线性表的第一个(首)结点,an称为线性表的最后一个(尾)结点。

基本说明:线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。 注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储) 但是把最后一个数据元素的尾指针指向了首位结点)。

基本特点:

  1. 存在一个唯一的被称为第一个的数据元素
  2. 存在一个唯一的被称为最后一个的数据元素
  3. 除第一个元素外,每个元素均有唯一一个直接前驱
  4. 除最后一个元素外,每个元素均有唯一一个直接后继
<?php
/**
 * LinearList  线性表
 *
 * -------------------------------------------------------------
 * [线性表顺序存储]
 * =======
 * 把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里,用这种方法存储的线性表简称线性表。
 * -------------------------------------------------------------
 * [顺序存储的线性表的特点]
 * =======
 *    - 线性表的逻辑顺序与物理顺序一致;
 *    - 数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现。
 * -------------------------------------------------------------
 * @param array
 */
class LinearOrder extends ArrayObject
{
    const PRECURSOR_KEY    = 0;
    const PRECURSOR_VALUE  = 1;

    const SUBSEQUENT_KEY   = 0;
    const SUBSEQUENT_VALUE = 1;

    const ASSIGN_KEY       = 0;
    const DEFAULT_KEY      = 1;

    const DELETE_KEY       = 0;
    const DELETE_VALUE     = 1;

    /**
     * @var array|mixed 线性表
     */
    public $oll;

    /**
     * LinearList constructor.  线性表初始化
     *
     * @param array $oll
     */
    public function __construct($oll = array ())
    {
        echo '---------------------------'.PHP_EOL;
        echo var_export($oll).PHP_EOL;
        echo '---------------------------'.PHP_EOL;
        parent::__construct($oll);
        $this->oll = $this->getIterator();
    }

    /**
     * @return void 清空线性表
     */
    public function __destruct()
    {
        unset($this->oll);
    }

    /**
     * 调试用请无视
     *
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        $this->preDispatch(); // 调试用,请无视
        return call_user_func_array(array($this, $name), $arguments);
    }

    /**
     * 判断线性表是否为空
     *
     * @return boolean 为空返回true,否则返回false
     */
    protected function isEmpty()
    {
        return $this->getLength() > 0 ? false : true;
    }

    /**
     * 返回线性表的长度
     *
     * @return int
     */
    protected function getLength()
    {
        return $this->oll->count();
    }

    /**
     * 返回线性表中下标为$key的元素
     *
     * @param mixed $key 线性表元素的下标
     * @return mixed
     */
    protected function getElement($key)
    {
        return $this->oll->offsetGet($key);
    }

    /**
     * 返回线性表中某个元素的位置
     *
     * @param mixed $value 线性表中某个元素的值
     * @return int 从1开始,如果返回-1表示不存在该元素
     */
    protected function getElementPosition($value)
    {
        $i = 0;
        $this->oll->rewind();
        while ($this->oll->valid()) {
            $i++;
            if (strcmp($value, $this->oll->current()) === 0) {
                return $i;
            }
            $this->oll-> next();
        }
        return -1;
    }

    /**
     * 返回线性表中某个元素的直接前驱元素
     *
     * @param string $value 线性表中某个元素的值
     * @param int    $tag   如果$value为下标则为1, 如果$value为元素值则为0
     * @return array|bool        array('value'=>...)直接前驱元素值,array('key'=>...)直接前驱元素下标
     */
    protected function getElementPrecursor($value, $tag = self::PRECURSOR_VALUE)
    {
        $i    = 0;
        $prev = null;
        $this->oll->rewind();
        while ($this->oll->valid()) {
            $key     = $this->oll->key();
            $current = $this->oll->current();
            if (strcmp($key, $value) === 0) {
                if ($i == 1) {
                    return false;
                }
                return array ('value' => $this->getElement($prev), 'key' => $prev);
            }
            if ($tag == self::PRECURSOR_VALUE) {
                if (strcmp($current, $value) === 0) {
                    if ($i == 1) {
                        return false;
                    }
                    return array ('value' => $this->getElement($prev), 'key' => $prev);
                }
            }
            $i++;
            $prev = $this->oll->key();
            $this->oll->next();
        }
    }

    /**
     * 返回某个元素的直接后继元素
     *
     * @param string   $value $value线性表中某个元素的值
     * @param int $tag   如果$value为下标则为1,如果$value为元素值则为0
     * @return array|bool       array('value'=>...)直接后继元素值,array('key'=>...)直接后继元素下标
     */
    protected function getElementSubsequent($value, $tag = self::SUBSEQUENT_KEY)
    {
        $i   = 0;
        $len = $this->getLength();
        $this->oll->rewind();
        while ($this->oll->valid()) {
            $key     = $this->oll->key();
            $current = $this->oll->current();
            if ($tag == self::SUBSEQUENT_KEY) {
                if (strcmp($key, $value) == 0) {
                    if ($i == $len) {
                        return false;
                    }
                    $this->oll->next();
                    return array ('value' => $this->oll->current(), 'key' => $this->oll->key());
                }
            }
            if ($tag == self::SUBSEQUENT_VALUE) {
                if (strcmp($current, $value) == 0) {
                    if ($i == $len) {
                        return false;
                    }
                    $this->oll->next();
                    return array ('value' => $this->oll->current(), 'key' => $this->oll->key());
                }
            }
            $i++;
            $this->oll->next();
        }
        return false;
    }

    /**
     * 在指定位置插入一个新的结点
     *
     * @param string $p     新结点插入位置,从0开始
     * @param string $value 线性表新结点的值
     * @param null   $key   线性表新结点的下标
     * @param int    $tag   是否指定新结点的下标,1表示默认下标,0表示指定下标
     * @return bool         插入成功返回true,失败返回false
     */
    protected function getInsertElement($p, $value, $key = null, $tag = self::DEFAULT_KEY)
    {
        $p   = (int)$p;
        $i   = 0;
        if ($p > $this->getLength() || $p < 1) {
            return false;
        }
        $this->oll->rewind();
        while ($this->oll->valid()) {
            if ($i != $p) {
                $i++;
                $this->oll->next();
            }
            switch ($tag){
                case self::DEFAULT_KEY:
                    $this->oll->append($value);
                    break 2;
                case self::ASSIGN_KEY:
                    $this->oll->offsetSet($key, $value);
                    break 2;
            }
        }
        return true;
    }

    /**
     * 根据元素位置返回线性表中的某个元素
     *
     * @param mixed $position 元素位置从1开始
     * @return array|bool  array('value'=>...)元素值,array('key'=>...)元素下标
     */
    protected function getElemForPos($position)
    {
        $i        = 0;
        $len      = $this->getLength();
        $position = (int)$position;
        if ($position > $len || $position < 1) {
            return false;
        }
        $this->oll->rewind();
        while ($this->oll->valid()) {
            if ($i == $position) {
                return array ('value' => $this->oll->current(), 'key' => $this->oll->key());
            }
            $i++;
            $this->oll->next();
        }
    }

    /**
     * 根据下标或者元素值删除线性表中的某个元素
     *
     * @param mixed $value 元素下标或者值
     * @param int   $tag   0表示$value为下标,1表示$value为元素值
     * @return bool 成功返回true,失败返回false
     */
    protected function getDeleteElement($value, $tag = self::DELETE_KEY)
    {
        $this->oll->rewind();
        while ($this->oll->valid())
        {
            $key     = $this->oll->key();
            $current = $this->oll->current();
            if ($tag ==  self::DELETE_KEY) {
                if (strcmp($key, $value) === 0) {
                    $this->oll->offsetUnset($key);
                }
            }
            if ($tag == self::DELETE_VALUE) {
                if (strcmp($current, $value) === 0) {
                    $this->oll->offsetUnset($key);
                }
            }
            $this->oll->next();
        }
        return true;
    }

    /**
     * 根据元素位置删除线性表中的某个元素
     *
     * @param int $position 元素位置从1开始
     * @return bool 成功返回true,失败返回false
     */
    protected function getDeleteEleForPos($position)
    {
        $len      = $this->getLength();
        $i        = 0;
        $position = (int)$position;
        if ($position > $len || $position < 1) {
            return false;
        }
        $this->oll->rewind();
        while ($this->oll->valid()) {
            $key     = $this->oll->key();
            if ($i == $position) {
                $this->oll->offsetUnset($key);
            }
            $i++;
            $this->oll->next();
        }
        return true;
    }

    /**
     * 调试用
     *
     * @param bool $isDebug
     * @return bool
     */
    protected function preDispatch($isDebug = true)
    {
        if (!$isDebug) {
            return false;
        }
        $debug      = debug_backtrace()[1];
        $reflection = new ReflectionMethod($this, $debug['args'][0]);
        $args       = '';
        if(isset($debug['args'][1])){
            foreach ($debug['args'][1] as &$value){
                if( is_array($value )){
                    $args .= json_encode($debug['args'][1]).', ';
                }else{
                    $args .= $value.', ';
                }
            }
        }
        $args = trim($args,', ');
        echo "\t".$reflection->getDocComment() . PHP_EOL;
        echo "\t{$debug['args'][0]}({$args})\n" . PHP_EOL;
    }
}

$echo = function ($str, $action) {
    echo $str . "\t->\t" . var_export($action, true) . PHP_EOL;
    echo "--------------------------- " . PHP_EOL;
};

$oll = new LinearOrder(array ('name' => 'Jack', 10, "age", 'msg' => 10, 666));
$echo('判断线性表是否为空', $oll->isEmpty());
$echo('返回线性表的长度', $oll->getLength());
$echo('根据下标返回线性表中的某个元素', $oll->getElement(1));
$echo('返回线性表中某个元素的位置', $oll->getElementPosition(666));
$echo('返回线性表中某个元素的直接前驱元素', $oll->getElementPrecursor(666, LinearOrder::PRECURSOR_VALUE));
$echo('返回线性表中某个元素的直接后继元素', $oll->getElementSubsequent(0, LinearOrder::SUBSEQUENT_KEY));
$echo('根据元素位置返回线性表中的某个元素', $oll->getElemForPos(2));
$echo('根据下标或者元素值删除线性表中的某个元素', $oll->getDeleteElement('name', LinearOrder::DELETE_KEY));
$echo('在指定位置插入一个新的结点', $oll->getInsertElement(3, "插入新节点", "qzone", LinearOrder::ASSIGN_KEY));
$echo('$oll->oll的内容 ', $oll->oll);

线性表顺序存储用到了Iterator:

/**
 * Interface for external iterators or objects that can be iterated
 * themselves internally.
 * @link https://php.net/manual/en/class.iterator.php
 */
interface Iterator extends Traversable {

    /**
     * Return the current element
     * @link https://php.net/manual/en/iterator.current.php
     * @return mixed Can return any type.
     */
    public function current();

    /**
     * Move forward to next element
     * @link https://php.net/manual/en/iterator.next.php
     * @return void Any returned value is ignored.
     */
    public function next();

    /**
     * Return the key of the current element
     * @link https://php.net/manual/en/iterator.key.php
     * @return string|float|int|bool|null scalar on success, or null on failure.
     */
    public function key();

    /**
     * Checks if current position is valid
     * @link https://php.net/manual/en/iterator.valid.php
     * @return bool The return value will be casted to boolean and then evaluated.
     * Returns true on success or false on failure.
     */
    public function valid();

    /**
     * Rewind the Iterator to the first element
     * @link https://php.net/manual/en/iterator.rewind.php
     * @return void Any returned value is ignored.
     */
    public function rewind();
}

继承的ArrayObject类:

/**
 * This class allows objects to work as arrays.
 * @link https://php.net/manual/en/class.arrayobject.php
 */
class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable {
    /**
     * Properties of the object have their normal functionality when accessed as list (var_dump, foreach, etc.).
     */
    const STD_PROP_LIST = 1;

    /**
     * Entries can be accessed as properties (read and write).
     */
    const ARRAY_AS_PROPS = 2;


    /**
     * Construct a new array object
     * @link https://php.net/manual/en/arrayobject.construct.php
     * @param array|object $array The input parameter accepts an array or an Object.
     * @param int $flags Flags to control the behaviour of the ArrayObject object.
     * @param string $iteratorClass Specify the class that will be used for iteration of the ArrayObject object. ArrayIterator is the default class used.
     *
     */
    public function __construct($array = array(), $flags = 0, $iteratorClass = "ArrayIterator") { }

    /**
     * Returns whether the requested index exists
     * @link https://php.net/manual/en/arrayobject.offsetexists.php
     * @param mixed $key <p>
     * The index being checked.
     * </p>
     * @return bool true if the requested index exists, otherwise false
     */
    public function offsetExists($key) { }

    /**
     * Returns the value at the specified index
     * @link https://php.net/manual/en/arrayobject.offsetget.php
     * @param mixed $key <p>
     * The index with the value.
     * </p>
     * @return mixed|false The value at the specified index or false.
     */
    public function offsetGet($key) { }

    /**
     * Sets the value at the specified index to newval
     * @link https://php.net/manual/en/arrayobject.offsetset.php
     * @param mixed $key <p>
     * The index being set.
     * </p>
     * @param mixed $value <p>
     * The new value for the <i>index</i>.
     * </p>
     * @return void
     */
    public function offsetSet($key, $value) { }

    /**
     * Unsets the value at the specified index
     * @link https://php.net/manual/en/arrayobject.offsetunset.php
     * @param mixed $key <p>
     * The index being unset.
     * </p>
     * @return void
     */
    public function offsetUnset($key) { }

    /**
     * Appends the value
     * @link https://php.net/manual/en/arrayobject.append.php
     * @param mixed $value <p>
     * The value being appended.
     * </p>
     * @return void
     */
    public function append($value) { }

    /**
     * Creates a copy of the ArrayObject.
     * @link https://php.net/manual/en/arrayobject.getarraycopy.php
     * @return array a copy of the array. When the <b>ArrayObject</b> refers to an object
     * an array of the public properties of that object will be returned.
     */
    public function getArrayCopy() { }

    /**
     * Get the number of public properties in the ArrayObject
     * When the <b>ArrayObject</b> is constructed from an array all properties are public.
     * @link https://php.net/manual/en/arrayobject.count.php
     * @return int The number of public properties in the ArrayObject.
     */
    public function count() { }

    /**
     * Gets the behavior flags.
     * @link https://php.net/manual/en/arrayobject.getflags.php
     * @return int the behavior flags of the ArrayObject.
     */
    public function getFlags() { }

    /**
     * Sets the behavior flags.
     * @link https://php.net/manual/en/arrayobject.setflags.php
     * @param int $flags <p>
     * The new ArrayObject behavior.
     * It takes on either a bitmask, or named constants. Using named
     * constants is strongly encouraged to ensure compatibility for future
     * versions.
     * </p>
     * <p>
     * The available behavior flags are listed below. The actual
     * meanings of these flags are described in the
     * predefined constants.
     * <table>
     * ArrayObject behavior flags
     * <tr valign="top">
     * <td>value</td>
     * <td>constant</td>
     * </tr>
     * <tr valign="top">
     * <td>1</td>
     * <td>
     * ArrayObject::STD_PROP_LIST
     * </td>
     * </tr>
     * <tr valign="top">
     * <td>2</td>
     * <td>
     * ArrayObject::ARRAY_AS_PROPS
     * </td>
     * </tr>
     * </table>
     * </p>
     * @return void
     */
    public function setFlags($flags) { }

    /**
     * Sort the entries by value
     * @link https://php.net/manual/en/arrayobject.asort.php
     * @param int $flags [optional]
     * @return void
     */
    public function asort($flags = SORT_REGULAR) { }

    /**
     * Sort the entries by key
     * @link https://php.net/manual/en/arrayobject.ksort.php
     * @param int $flags [optional]
     * @return void
     */
    public function ksort($flags = SORT_REGULAR) { }

    /**
     * Sort the entries with a user-defined comparison function and maintain key association
     * @link https://php.net/manual/en/arrayobject.uasort.php
     * @param callback $callback <p>
     * Function <i>cmp_function</i> should accept two
     * parameters which will be filled by pairs of entries.
     * The comparison function must return an integer less than, equal
     * to, or greater than zero if the first argument is considered to
     * be respectively less than, equal to, or greater than the
     * second.
     * </p>
     * @return void
     */
    public function uasort($callback) { }

    /**
     * Sort the entries by keys using a user-defined comparison function
     * @link https://php.net/manual/en/arrayobject.uksort.php
     * @param callback $callback <p>
     * The callback comparison function.
     * </p>
     * <p>
     * Function <i>cmp_function</i> should accept two
     * parameters which will be filled by pairs of entry keys.
     * The comparison function must return an integer less than, equal
     * to, or greater than zero if the first argument is considered to
     * be respectively less than, equal to, or greater than the
     * second.
     * </p>
     * @return void
     */
    public function uksort($callback) { }

    /**
     * Sort entries using a "natural order" algorithm
     * @link https://php.net/manual/en/arrayobject.natsort.php
     * @return void
     */
    public function natsort() { }

    /**
     * Sort an array using a case insensitive "natural order" algorithm
     * @link https://php.net/manual/en/arrayobject.natcasesort.php
     * @return void
     */
    public function natcasesort() { }

    /**
     * Unserialize an ArrayObject
     * @link https://php.net/manual/en/arrayobject.unserialize.php
     * @param string $data <p>
     * The serialized <b>ArrayObject</b>.
     * </p>
     * @return void The unserialized <b>ArrayObject</b>.
     */
    public function unserialize($data) { }

    /**
     * Serialize an ArrayObject
     * @link https://php.net/manual/en/arrayobject.serialize.php
     * @return string The serialized representation of the <b>ArrayObject</b>.
     */
    public function serialize() { }

    /**
     * @return array
     * @since 7.4
     */
    public function __debugInfo(){}


    /**
     * @return array
     * @since 7.4
     */
    public function __serialize(): array {}

    /**
     * @param array $data
     * @since 7.4
     */
    public function __unserialize(array $data): void {}

    /**
     * Create a new iterator from an ArrayObject instance
     * @link https://php.net/manual/en/arrayobject.getiterator.php
     * @return ArrayIterator An iterator from an <b>ArrayObject</b>.
     */
    public function getIterator() { }

    /**
     * Exchange the array for another one.
     * @link https://php.net/manual/en/arrayobject.exchangearray.php
     * @param mixed $array <p>
     * The new array or object to exchange with the current array.
     * </p>
     * @return array the old array.
     */
    public function exchangeArray($array) { }

    /**
     * Sets the iterator classname for the ArrayObject.
     * @link https://php.net/manual/en/arrayobject.setiteratorclass.php
     * @param string $iteratorClass <p>
     * The classname of the array iterator to use when iterating over this object.
     * </p>
     * @return void
     */
    public function setIteratorClass($iteratorClass) { }

    /**
     * Gets the iterator classname for the ArrayObject.
     * @link https://php.net/manual/en/arrayobject.getiteratorclass.php
     * @return string the iterator class name that is used to iterate over this object.
     */
    public function getIteratorClass() { }
}

线性表 单链存储

<?php
/**
 * LinearChain  线性表 - 单链表存储
 * [线性表单链存储]
 * =======
 * 用一组任意的存储单元存储线性表中的数据元素,用这种方法存储的线性表简称线性链表。
 * -------------------------------------------------------------
 * [单链存储的线性表的特点]
 * =======
 *    - 存储链表中结点的一组任意的存储单元可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上
 *    - 链表中结点的逻辑顺序和物理顺序不一定相同
 * -------------------------------------------------------------
 */
class LinearChain
{
    /**
     * @var  string 头结点指针
     */
    public $next;

    /**
     * @var string 结点数据
     */
    public $elem;

    /**
     * @var int 链表长度
     */
    public $length;

    /**
     * LinearChain constructor.  线性表初始化
     */
    public function __construct()
    {
        $this->next = $this->elem = $this->length = null;
    }

    /**
     * 清空单链表
     *
     * @return mixed
     */
    public function clearChain()
    {
        if ($this->length <= 0) {
            return false;
        }
        while ($this->next != null) {
            $q          = $this->next->next;
            $this->next = null;
            unset($this->next);
            $this->next = $q;
        }
        $this->length = 0;
    }

    /**
     * 返回单链表长度
     *
     * @return mixed
     */
    public function getLength()
    {
        return $this->length;
    }

    /**
     * 判断单链表是否为空
     *
     * @return bool 为空返回true,不为空返回false
     */
    public function getIsEmpty()
    {
        return $this->length == 0 && $this->next == null;
    }

    /**
     * 头插入法建表
     *
     * @param array $arr 建立单链表的数据
     * @return mixed
     */
    protected function getHeadCreateChain(array $arr)
    {
        $this->clearChain();
        $iterator = $this->generator($arr);
        $iterator->rewind();
        while ($iterator->valid()) {
            $node       = new stdClass();
            $node->elem = $iterator->current();
            $node->next = $this->next;
            $this->next = $node;
            $this->length++;
            $iterator->next();
        }
        return $this->next;
    }

    /**
     * 尾插入法建表
     *
     * @param array $arr 建立单链表的数据
     * @return mixed
     */
    protected function getTailCreateChain(array $arr)
    {
        $this->clearChain();
        $pre_node     = $this;  // 上个节点指向头节点
        $iterator = $this->generator($arr);
        $iterator->rewind();
        while ($iterator->valid()) {
            $node       = new stdClass();
            $node->elem = $iterator->current();
            $node->next = $pre_node->next;  // $pre_node->next 指向null
            $pre_node->next = $node;  // $pre_node->next 上个节点的 下个节点 指向 这个节点
            $pre_node       = $node;  // $pre_node 换为 这个节点
            $iterator->next();
            $this->length++;
        }
        
        return $this->next;
    }

    /**
     * 返回第$i个元素,头插法实现
     *
     * @param int $i 元素位序,从1开始
     * @return mixed
     */
    protected function getElemForPos($i)
    {
        $i = (int)$i;
        if ($i > $this->length || $i < 1) {
            return null;
        }
        $mark = 1;
        $self = $this->next;
        while ($mark < $i) {
            $box  = $self->next;
            $self = $box;
            $mark++;
        }
        return $self;
    }

    /**
     * 查找单链表中是否存在某个值的元素
     * 如果有,返回该元素结点,否则返回null
     *
     * @param mixed $value 查找的值
     * @return mixed
     */
    protected function getElemIsExist($value)
    {
        $self = $this;
        while ($self->next != null && strcmp($self->elem, $value) !== 0) {
            $self = $self->next;
        }
        if (strcmp($self->elem, $value) === 0) {
            return $self;
        }
        return null;
    }

    /**
     * 查找单链表中是否存在某个值的元素
     * 如果有,返回该元素位序,否则返回-1
     *
     * @param mixed $value 查找的值
     * @return mixed
     */
    protected function getElemPosition($value)
    {
        $self = $this;
        $mark = 0;
        while ($self->next != null && strcmp($self->elem, $value) !== 0) {
            $self = $self->next;
            $mark++;
        }
        if (strcmp($self->elem, $value) === 0) {
            return $mark;
        }
        return -1;
    }

    /**
     * 单链表的插入操作
     *
     * @param int   $key   插入元素的位序,即在什么位置插入新的元素,从1开始
     * @param mixed $value 插入的新的元素值
     * @return boolean 插入成功返回true,失败返回false
     */
    protected function getInsertElem($key, $value)
    {
        if ($key > $this->length || $key < 1) {
            return false;
        }
        $mark = 1;
        $self = $this;
        while ($self->next != null && $mark < $key) {
            $self = $self->next;
            $mark++;
        }
        $node       = new stdClass();
        $node->elem = $value;
        $node->next = $self->next;
        $self->next = $node;
        $this->length++;
        return true;
    }

    /**
     * 遍历单链表中的所有元素
     *
     * @return array 包括单链中的所有元素
     */
    protected function getAllElem()
    {
        $result = array ();
        if ($this->getIsEmpty()) {
            return $result;
        }
        $self = $this->next;
        while ($self->next != null) {
            array_push($result, $self->elem);
            $self = $self->next;
        }
        array_push($result, $self->elem);
        return $result;
    }

    /**
     * 根据Key 删除单链中的元素
     *
     * @param int $key 元素位序
     * @return boolean 删除成功返回true,失败返回false
     */
    protected function getDeleteElem($key)
    {
        $key = (int)$key;
        if ($key > $this->length || $key < 1) {
            return false;
        }
        $self = $this;
        $mark = 1;
        while ($mark < $key) {
            $self = $self->next;
            $mark++;
        }
        $node       = $self->next;
        $self->next = $node->next;
        $this->length--;
        return true;
    }

    /**
     * 删除单链表中值为$value的前 $i($i>=1) 个结点
     *
     * @param mixed mixed 待查找的值
     * @param $i    mixed 删除的次数,即删除查找到的前$i个
     * @return mixed
     */
    protected function getDeleteElemForValue($value, $i = 1)
    {
        if ($i > 1) {
            $this->getDeleteElemForValue($value, $i - 1);
        }
        $vp = $this->getElemPosition($value);
        $this->getDeleteElem($vp);
        return $this->getAllElem();
    }

    /**
     * 删除单链表所有重复的值
     *
     * @return mixed
     */
    protected function getElemUnique()
    {
        if ($this->getIsEmpty()) {
            return $this->getAllElem();
        }
        $self = $this;
        while ($self->next != null) {
            $node = $self->next;
            $ptr  = $self;
            while ($node->next != null) {
                if (strcmp($self->elem, $node->elem) === 0) {
                    $ptr->next = $node->next;
                    unset($node->next);
                    $node = $ptr->next;
                    $this->length--;
                } else {
                    $ptr  = $node;
                    $node = $node->next;
                }
            }
            $self = $self->next;
        }
        return $this->getAllElem();
    }

    /**
     * 迭代器生产
     *
     * @param array $info
     * @return \ArrayIterator
     */
    protected function generator(array $info)
    {
        return (new ArrayObject($info))->getIterator();
    }

    /**
     * 调试用请无视
     *
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        $this->preDispatch(); // 调试用,请无视
        return call_user_func_array(array ($this, $name), $arguments);
    }

    /**
     * 调试用请无视!
     *
     * @param bool $isDebug
     * @return bool
     */
    protected function preDispatch($isDebug = true)
    {
        if (!$isDebug) {
            return false;
        }
        $debug      = debug_backtrace()[1];
        $reflection = new ReflectionMethod($this, $debug['args'][0]);
        $args       = '';
        if (isset($debug['args'][1])) {
            foreach ($debug['args'][1] as &$value) {
                if (is_array($value)) {
                    $args .= json_encode($debug['args'][1]) . ', ';
                } else {
                    $args .= $value . ', ';
                }
            }
        }
        $args = trim($args, ', ');
        echo "\t" . $reflection->getDocComment() . PHP_EOL;
        echo "\t{$debug['args'][0]}({$args})\n" . PHP_EOL;
    }
}

$echo = function ($str, $action) {
    echo $str . "\t->\t" . var_export($action, true) . PHP_EOL;
    echo "--------------------------- " . PHP_EOL;
};
$personal = array (
    "One",
    "Two",
    "Three",
    "Four",
    "Five",
);

$oll = new LinearChain();
$echo('头插入链表数据', $oll->getHeadCreateChain($personal));
$echo('尾插入链表数据', $oll->getTailCreateChain($personal));
$echo('返回第二个数据', $oll->getElemForPos(2));
$echo('是否存在Tow呢?', $oll->getElemIsExist("Two"));
$echo('One 的下标示是', $oll->getElemPosition("One"));
$echo('从2号位插入一个Four', $oll->getInsertElem(2, "Four"));
$echo('遍历整个单链表', $oll->getAllElem());
$echo('删除第一个元素', $oll->getDeleteElem(1));
$echo('删除Three 前一个', $oll->getDeleteElemForValue("Three", 1));
$echo('去重链表中重复值', $oll->getElemUnique());

堆栈

栈(Stack):是限制在表的一端进行插入和删除操作的线性表。又称为后进先出LIFO (Last In First Out)或先进后出FILO (First In Last Out)线性表。

<?php
/**
 * -------------------------------------------------------------
 * [栈的定义]
 * =======
 * 栈(Stack):是限制在表的一端进行插入和删除操作的线性表。又称为后进先出LIFO (Last In First Out)或先进后出FILO (First In Last Out)线性表。
 * 栈顶(Top):允许进行插入、删除操作的一端,又称为表尾。用栈顶指针(top)来指示栈顶元素。
 * 栈底(Bottom):是固定端,又称为表头。
 * 空栈:当表中没有元素时称为空栈。
 * [栈的实现方式]
 * =======
 *   - 硬堆栈:利用CPU中的某些寄存器组或类似的硬件或使用内存的特殊区域来实现。这类堆栈容量有限,但速度很快;
 *   - 软堆栈:这类堆栈主要在内存中实现。堆栈容量可以达到很大。在实现方式上,又有动态方式和静态方式两种
 * -------------------------------------------------------------
 * [定义]
 * =======
 * 线性表(Linear List) :是由n(n≧0)个数据元素(结点) [a1,a2, …an] 组成的有限序列。数据元素是一个抽象的符号,其具体含义在不同的情况下一般不同。
 * 该序列中的所有结点具有相同的数据类型。其中数据元素的个数n称为线性表的长度。
 * 当n=0时,称为空表。
 * 当n>0时,将非空的线性表记作: (a1,a2,…an) a1称为线性表的第一个(首)结点,an称为线性表的最后一个(尾)结点。
 * -------------------------------------------------------------
 * [线性表顺序存储]
 * =======
 * 把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里,用这种方法存储的线性表简称线性表。
 * -------------------------------------------------------------
 * [顺序存储的线性表的特点]
 * =======
 *    - 线性表的逻辑顺序与物理顺序一致;
 *    - 数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现。
 * -------------------------------------------------------------
 * @param array
 */
class StackExample
{
    /**
     * @var null   栈元素
     */
    protected $elem;
    /**
     * @var null 下一个节点
     */
    protected $next;
    /**
     * @var int 栈长度
     */
    protected $length;

    /**
     * 初始化栈
     * StackExample constructor.
     */
    public function __construct()
    {
        $this->elem   = $this->next = null;
        $this->length = 0;
    }

    /**
     * 判断栈是否空栈
     *
     * @return boolean 如果为空栈返回true,否则返回false
     */
    protected function getIsEmpty()
    {
        return $this->elem ? false : true;
    }

    /**
     * 将所有元素出栈
     *
     * @return array 返回所有栈内元素
     */
    protected function getAllPopStack()
    {
        $result = array ();
        if ($this->getIsEmpty()) {
            return $result;
        }
        while ($this->elem != null) {
            $result[]   = $this->elem->elem;
            $this->elem = $this->elem->next;
        }
        $this->length = 0;
        return $result;
    }

    /**
     * 返回栈内元素个数
     *
     * @return int
     */
    protected function getLength()
    {
        return $this->length;
    }

    /**
     * 元素进栈
     *
     * @param mixed $element 进栈元素值
     * @return void
     **/
    protected function setPushStack($element)
    {
        $stack       = new stdClass();
        $stack->elem = $element;
        $stack->next = $this->elem;
        $this->elem  = &$stack;
        $this->length++;
    }

    /**
     * 元素出栈
     *
     * @return boolean 出栈成功返回true,否则返回false
     **/
    protected function getPopStack()
    {
        if ($this->getIsEmpty()) {
            return false;
        }
        $node       = $this->elem;
        $this->elem = $node->next;
        $this->length--;
        
        return $node;
    }

    /**
     * 仅返回栈内所有元素
     *
     * @return array 栈内所有元素组成的一个数组
     */
    protected function getAllElem()
    {
        $result = array ();
        if ($this->getIsEmpty()) {
            return $result;
        }
        $node = $this->elem;
        while ($node != null) {
            $result[] = $node->elem;
            $node     = $node->next;
        }
        return $result;
    }

    /**
     * 返回栈内某个元素的个数
     *
     * @param mixed $elem 待查找的元素的值
     * @return int
     **/
    protected function getCountForElem($elem)
    {
        $result = $this->getAllElem();
        $count  = 0;
        foreach ($result as $value) {
            if ($elem === $value) {
                $count++;
            }
        }
        return $count;
    }

    /**
     * 调试用请无视
     *
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        $this->preDispatch(); // 调试用,请无视
        return call_user_func_array(array ($this, $name), $arguments);
    }

    /**
     * 调试用
     *
     * @param bool $isDebug
     * @return bool
     */
    protected function preDispatch($isDebug = true)
    {
        if (!$isDebug) {
            return false;
        }
        $debug      = debug_backtrace()[1];
        $reflection = new ReflectionMethod($this, $debug['args'][0]);
        $args       = '';
        if (isset($debug['args'][1])) {
            foreach ($debug['args'][1] as &$value) {
                if (is_array($value)) {
                    $args .= json_encode($debug['args'][1]) . ', ';
                } else {
                    $args .= $value . ', ';
                }
            }
        }
        $args = trim($args, ', ');
        echo "\t" . $reflection->getDocComment() . PHP_EOL;
        echo "\t{$debug['args'][0]}({$args})\n" . PHP_EOL;
    }
}

$echo = function ($str, $action) {
    echo $str . "\t->\t" . var_export($action, true) . PHP_EOL;
    echo "--------------------------- " . PHP_EOL;
};

调用:

$stack = new StackExample();
$stack->setPushStack('First');
$stack->setPushStack('Second');
$echo('返回栈内所有元素', $stack->getAllElem());
$stack->setPushStack('Third');
$echo('返回栈内所有元素', $stack->getAllElem());
$stack->getPopStack();
$echo('返回栈内所有元素', $stack->getAllElem());

输出:

返回栈内所有元素    ->    array (
  0 => 'Second',
  1 => 'First',
)
--------------------------- 
返回栈内所有元素    ->    array (
  0 => 'Third',
  1 => 'Second',
  2 => 'First',
)
--------------------------- 
返回栈内所有元素    ->    array (
  0 => 'Second',
  1 => 'First',
)
--------------------------- 

我们也可以使用php的Spl标准库的SplStack类实现栈。

SplStack类通过使用一个双向链表(SplDoublyLinkedList)来提供栈的主要功能。

<?php
//SplStack Mode is LIFO (Last In First Out)
 
$q = new SplStack();

$q[] = 1;
$q[] = 2;
$q[] = 3;
$q->push(4);
$q->add(4,5);

$q->rewind();
while($q->valid()){
    echo $q->current(),"\n";
    $q->next();
}
?>

输出:

5
4
3
2
1
<?php
/**
 * KitchenQueue
 * -------------------------------------------------------------
 * [游戏说明]
 * 假设你要为饭店创建一个接受顾客点菜单点应用程序,这个应用程序存储一系列点菜服务,服务员添加菜单,
 * 而厨师取出菜单并制作菜肴
 * -------------------------------------------------------------
 *
 * @license   MIT
 */

class KitchenQueue
{
    /**
     * @var \stdClass $cooking
     */
    protected $cooking;

    /**
     * 服务员
     *
     * @param $dishes
     */
    public function waiter($dishes)
    {
        $node          = new \stdClass();
        $node->element = $dishes;
        $node->next    = $this->cooking;
        $this->cooking = $node;
    }

    /**
     * 厨师
     *
     * @return \stdClass
     */
    public function kitchen()
    {
        return $this->cooking;
    }
}

$kitchen = new KitchenQueue();
$kitchen->waiter("Qin Jiao Rou Si");
$kitchen->waiter("Ma Po Dou Fu");
$kitchen->waiter("Fu Qi Fei Pian");
$kitchen->waiter("Kou Shui Ji");
var_dump($kitchen->kitchen());

参考资料

PHP 手册 函数参考 变量与类型相关扩展 数组 数组 函数 https://www.php.net/manual/zh/ref.array.php

用 PHP 的方式实现的各类算法合集 https://github.com/m9rco/algorithm-php

PHP实现八大算法 https://www.cnblogs.com/zixuanfy/p/7617451.html

PHP的基本算法合集 https://www.php.cn/php-weizijiaocheng-394062.html

PHP算法 https://www.jianshu.com/p/cb2ad99029c8

php数据结构和算法 https://www.csdn.net/gather_21/MtTacg3sMzkxNy1ibG9n.html

七大常用PHP算法 http://www.sohu.com/a/118661179_468191

堆排序算法与PHP实现 https://www.cnblogs.com/iampeter/p/3223487.html

PHP实现排序算法—-堆排序(Heap Sort) https://blog.csdn.net/baidu_30000217/article/details/53087079

PHP实现堆排序 https://www.cnblogs.com/zhenbianshu/p/5273995.html

哪本《数据结构与算法》最好? https://www.zhihu.com/question/21628833/answer/54623559

数据结构与算法必备的 50 个代码实现 https://zhuanlan.zhihu.com/p/67490380

常见数据结构与算法整理总结(上) https://www.cnblogs.com/xkzhangsanx/p/10888179.html

PHP实现各种经典算法 http://www.cnblogs.com/hellohell/p/5718175.html

php面试题之二——数据结构和算法(高级部分) https://blog.csdn.net/s1070/article/details/51174725

阮一峰 字符串匹配的KMP算法 http://www.ruanyifeng.com/blog/2013/05/Knuth–Morris–Pratt_algorithm.html

从头到尾彻底理解 KMP https://wiki.jikexueyuan.com/project/kmp-algorithm/

KMP算法—终于全部弄懂了 https://blog.csdn.net/dark_cy/article/details/88698736

PHP 手册 函数参考 其它基本扩展 SPL 数据结构 The SplStack class https://www.php.net/manual/zh/class.splstack.php


返回