php实现双向链表(LinkedList)

**仅作为个人笔记记录一下,有任何不足之处,欢迎大神们多多指正!!!**

1. 定义:

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。


2. 数据格式示意图:

在这里插入图片描述
在这里插入图片描述

大致就是那么个意思,每个节点都保存着前节点、后节点的地址值以及data值,头结点、尾结点比较特殊,头结点的前节点地址为null、尾结点的后节点地址值为null。


3. 实现代码:

<?php
/**
 * Class Node
 * 节点类
 */
class Node{
    public $previous = null; //前驱指针
    public $val = null; //节点的val
    public $next = null; //后驱指针

    /**
     * Node constructor.
     * @param $val
     */
    public function __construct($val){
        $this->val = $val;
    }

    /**
     * 重写toString
     * @return string
     */
    public function __toString(){
        // TODO: Implement __toString() method.
        return "[{$this->val}]";
    }


}

/**
 * Class linkedList
 *
 * 节点操作类
 */
class linkedList{
    private $headNode; //头指针
    private $tailNode; //尾指针
    private $size; //链表长度

    private static $instance = null; //linkedList操作实例


    /**
     * linkedList constructor.
     */
    private function __construct(){
        $this->size = 0;
    }

    /**
     * @return linkedList
     */
    public static function instance(){
        if(self::$instance instanceof self){
            return self::$instance;
        }
        self::$instance = new self();
        return self::$instance;
    }

#region     1. 添加节点 - 默认是从后向前添加
    /**
     * 新增节点
     *      - 插入规则:如果index存在,则插入的节点替换已存在的index索引节点,被替换的节点后移
     * @param mixed $element 元素
     * @param null|int $index
     * @return bool
     * @throws Exception
     */
    public function add($element, $index = null){
        $node = new Node($element);
        //检测越界
        $this->_checkRange($index);
        //添加节点
        if(is_null($index)){ //index传null,表示从末尾添加
            if($this->tailNode instanceof Node){ //尾结点也是一个Node,即链表中已经有了元素
                $node->previous = $this->tailNode;
                $this->tailNode->next = $node;
                $this->tailNode = $node;
            }else{ //尾结点不是Node,null表示链表中没有元素
                $this->headNode = $this->tailNode = $node;
            }
        }else{
            $nodeOld = $this->get($index); //当前索引上的老节点
            //替换为参数节点
            if($nodeOld instanceof Node){ //如果被替换的节点是Node的实例
                $node->previous = $nodeOld->previous;
                $node->next = $nodeOld;
                $nodeOld->previous = $node;
            }
            //判断是否为头节点 or 尾结点
            if($node->previous === null){
                $this->headNode = $node;
            }
            if($node->next === null){
                $this->tailNode = $node;
            }
        }

        $this->size ++;
        return true;
    }
#endregion


#region     2. 删除节点
    /**
     * 删除节点
     * @param int $index 节点的索引值
     * @return bool
     * @throws Exception
     */
    public function remove($index){
        //检测索引值有没有越界
        $this->_checkRange($index);

        $node = $this->headNode;
        $countIndex = 0; //计数索引
        while ($node instanceof Node){
            if($countIndex == $index){ //找到了指定索引的节点
                //前驱存在
                if($node->previous instanceof Node ){
                    if( $node->next instanceof Node){ //后驱存在
                        $node->previous->next = $node->next;
                    }else{ //后驱不存在,尾节点置为null
                        $node->previous->next = null;
                        $this->tailNode = null;
                    }
                }
                //后驱存在
                if($node->next instanceof Node){
                    if($node->previous instanceof Node){
                        $node->next->previous = $node->previous ; //前驱存在
                    }else{
                        $node->next->previous = null; //前驱不存在
                        $this->headNode = $node->next; //头节点置为下一个节点
                    }
                }
                unset($node);
                $this->size --;
                return true;
            }
            $node = $node->next; //指针向下移动一位,指向下一个节点
            $countIndex ++; //计数索引 +1
        }

        return false;
    }
#endregion


#region     3. 获取节点
    /**
     * 从头开始找指定索引的节点
     * @param int $index 索引值
     * @return Node|null
     */
    public function get($index){
        if(!is_numeric($index)) return null;

        //检测索引值有没有越界
        $this->_checkRange($index);

        $node = $this->headNode;
        $countIndex = 0; //计数索引
        while ($node instanceof Node){
            if($countIndex == $index){
                return $node;
            }
            $node = $node->next;
            $countIndex ++;
        }
        return null;
    }
#endregion


#region     4. 格式化展示链表数据
    /**
     * 格式化展示链表
     * @return string
     * ⇋ ⇌ ⇆
     */
    public function formatShow(){
        $str = "";
        $node = $this->headNode;
        $countIndex = 0; //计数索引
        $connector = " ⇆ "; //连接符
        while ($node instanceof Node){
            $str .= "[{$countIndex}:{$node->val}]{$connector}";
            $node = $node->next;
            $countIndex ++; //+1
        }
        return trim($str, $connector);
    }
#endregion


#region     5. 获取链表元素格式
    /**
     * @return int
     */
    public function size(){
        return $this->size;
    }
#endregion


#region     6. 获取节点的索引值(如果有值相同的节点,则返回匹配到的第一个节点索引值)
    /**
     * @param $element
     * @return int
     */
    public function indexOf($element){
        $node = $this->headNode;
        $countIndex = 0;
        while ($node instanceof Node){
            if($node->val == $element){
                return $countIndex;
            }
            $node = $node->next;
            $countIndex ++;
        }

        return -1; //没找到,返回-1
    }
#endregion

    /**
     * 检测范围
     * @param int|null $index 传递传索引值
     * @throws Exception
     */
    private function _checkRange($index){
        //检测索引值有没有越界
        if($index && $index >= $this->size) throw new Exception("indexOutOfBounds: index越界!");
    }
}



//-----------------------------------------------------------------------------------------------------------------
//  Debug调试
//-----------------------------------------------------------------------------------------------------------------
try{
    linkedList::instance()->add("val_1");
    linkedList::instance()->add("val_2");
    linkedList::instance()->add("val_3");
    linkedList::instance()->add("val_4");
    linkedList::instance()->add("val_5");
    linkedList::instance()->add("val_6");
    linkedList::instance()->add("val_x");
    linkedList::instance()->add("val_x");
    linkedList::instance()->add("val_x");

    $linkedString = linkedList::instance()->formatShow();
    echo "size : ",linkedList::instance()->size(),PHP_EOL;
    echo "linkedString : " , $linkedString,PHP_EOL;


    echo "获取指定索引的节点:", linkedList::instance()->get(1),PHP_EOL;
    echo "删除指定索引的节点:", linkedList::instance()->remove(4),PHP_EOL;
    echo "size : ",linkedList::instance()->size(),PHP_EOL;
    echo "linkedString : " , linkedList::instance()->formatShow(),PHP_EOL;
    echo "查找节点索引 : " , linkedList::instance()->indexOf("val_x"),PHP_EOL;


    if(error_get_last() !== null){
        throw new Exception(json_encode(error_get_last()));
    }
}catch (Exception $exception){
    $errMsg = [
        'code' => $exception->getCode(),
        'msg' => $exception->getMessage(),
        'trace' => $exception->getTrace(),
        'trace_as_string' => $exception->getTraceAsString(),
    ];
    print_r($errMsg);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值