资讯专栏INFORMATION COLUMN

利用PHP实现常用的数据结构之链表(小白系列文章五)

rollback / 2408人阅读

摘要:因为涉及指针,我们用引用来模拟,所以读者应该有面向对象的知识贮备。引文因为涉及内存,常常会有一些程序的边界限制,需要拥有一定严密的逻辑去保证代码的鲁棒性和健壮性,所以这个知识点是面试的常考点。

tips:因为涉及指针,我们用引用来模拟,所以读者应该有面向对象的知识贮备。
引子

你可以把链表简单理解为动态数组,它不需要一块一块的开辟空间,同时,你又要注意,它存在的主要意义或者说使用场景主要是”指针功能“,它能够指来指去,对一些应用特别是内存管理起到了关键作用。

引文

因为涉及内存,常常会有一些程序的边界限制,需要programer拥有一定严密的逻辑去保证代码的鲁棒性和健壮性,所以这个知识点是面试的常考点。下面我们看看PHP的单链表实现(附常考题目实现):

data = $val;
        $this->next = $nex;
    }
}


/**
 * TODO:构建单链表 
 */
Class SingleLinkList{

    /*    头插法创建链表 n为节点总数    */
    public function headInsert($n){
        /*    新建一个头节点    */
        $head = new Node(null,null);
        for($i=$n;$i>0;$i--){
            $newNode = new Node($i,null);
            $head->data = $newNode->data;    #新建节点赋值给头节点
            $newNode->next = $head->next;    #将头节点的后继节点作为新建节点的后继节点,相当于在原头节点和头节点的后继节点中间添加了一个新节点
            $head->next = $newNode;            #将新建节点作为头节点的后继节点,这时候原本头节点的后继节点已经改变了
        }
        return $head;
    }

    /*    尾插法创建链表    */
    public function rearInsert($n){
        /*    新建一个尾节点    */
        $rear = new Node(null,null);
        for($j=0;$j<$n;$j++){
            $newNode = new Node($j,null);
            $rear->data = $newNode->data;

            //$newNode = $rear->next;
            $rear->next = $newNode;
            $rear = $newNode;
        }
        return $rear;
    }


    /**
     * TODO:读取链表中第i个数据
     * @param $list object 待插入的链表
     * @param $i     int     节点序号
     */

    public function readIThNode($list,$i){
        /*    如果链表为空或者i小等于0    */
        if($list == null || $i<=0){
            echo "输入参数不合法";
            return ;
        }
        /*        */
        $p = $list->next;    #设置p指向第一个节点(即头节点的后继节点))
        $j=0;                #计时器必须初始化
        while($p && $j<$i ){
            $p = $p->next;
            ++$j;
        }

        /*    第i步    */
        if($p == null){    #说明链表已经结束,不存在i节点,过滤掉i大于链表长度的情况(因为节点是散列的,事先并不知道其长度)
            echo "i长度大于链表长度" ;
            exit;
        }else{
            $e = $p->data;    #第i个节点存在 ,返回
            return $e;
        }

    }

    /**
     * TODO:在链表的第i个位置之前插入节点e
     * @param $list object 待插入的链表
     * @param $i     int     节点序号
     * @param $e    object 待插入的节点
     */
    public function Insert($list,$i,$e){
        if($e == null){
            echo "待插入节点为空";
            exit;
        }
        $p = $list->next;    #设置p指向第一个节点
        $j=0;                #计时器必须初始化

        while($p && $j<$i ){
            $p = $p->next;    #保证节点在向后移动
            ++$j;
        }

        /*    第i步    */
        if($p == null){    #说明链表已经结束,不存在i节点,过滤掉i大于链表长度的情况(因为节点是散列的,事先并不知道其长度)
            echo "不存在i节点" ;
            exit;
        }else{
            /*    标准的插入语句(头插法)    */
            $e->next = $p->next;
            $p->next = $e;
            return $list;
        }
    }


    /**
     * TODO:删除链表的第i个节点,并返回该节点的值
     * @param $list object 待插入的链表
     * @param $i    int    节点序号
     */
    public function Delete($list,$i){
        if($list == null || $i<=0){
            echo "输入参数不合法";
            exit;
        }
        $p = $list->next;    #设置p指向第一个节点
        $j=0;                #计时器必须初始化

        while($p && $j<$i ){
            $p = $p->next;    #保证节点在向后移动
            ++$j;
        }

        /*    第i步    */
        if($p == null){    #说明链表已经结束,不存在i节点,过滤掉i大于链表长度的情况,以为若i大于链表长度,则上面循环会跳出直接进入判断然后返回
            echo "不存在i节点" ;
            exit;
        }else{
            /*    标准的删除语句    */
            $q = $p->next;
            $p->next = $q->next;
            $e = $q->data;
            unset($q);
            return $e;
        }
    }

    /**
     * TODO:删除整张链表
     * @param $list object 待插入的链表
     */
    public function DeleteAll($list){
        if($list == null ){
            echo "输入参数不合法";
            exit;
        }
        $p = $list->next;    #设置p指向第一个节点
        
        while($p != null ){
            $q = $p->next;    #保证节点在向后移动
            unset($p);
            $p = $q;
        }
    }

    /**
     * Question1:输出倒数第K个节点
     * @param $head object 链表
     * @param $k     int    序号
     */
    function FindKthToTail($head, $k){
        /*    如果链表为空或者k不合法 返回null    */
        if($head == null || $k<=0){
            return null;
        }
        
        /*    这里采用了复杂度为O(n)的算法,需要准备两个节点    */
        $behind = $head;    #指向链表的第一个节点
       
        /*    算法思路:准备两个指针,假如第一个指针走到n-1(即链表末尾),第二个指针走到倒数k的位置,两者之间相差(n-1)-(n-k) = k-1 */
        for($i=0;$i<$k-1;$i++){
            /*    让第一个指针先走k-1个单位,如果不为空,则指针向后移动    */
            /*    注意:这里有一个隐藏的条件,就是链表的长度有可能小于k,我们不不遍历完整个链表是无法知道其长度的    */
            if($head->next != null){
                $head = $head->next;
            }else{
                return ;
            }
        }
        /*    当第一个指针走到k-1且还不为空,这时让第二个指针开始走,当第一个指针走到n-1的时候,第二个指针也走到了倒数第k的位置,即所求    */
        while($head->next != null){
            $head = $head->next;
            $behind = $behind->next;
        }
        return $behind;
    }

    
    /**
     * Question2:反转链表
     * @param $head object 链表
     */
    public function ReverseList($pHead)
{
    /*    如果链表为空,返回null    */
    if($pHead == null){
        return null;
    }
    $pre  = $pHead; #前一节点 ,这里是根节点
    $cur  = $pre->next; #当前节点    2     例:1->2->3
    $next = null;    #后一节点
   
    /*    链表存在且不为空    */
    while(!$cur){
        $next = $cur->next;    #用一个变量暂时存储后一节点,因为一旦前面反转,就断链了
        $cur->next = $pre;    #将前一节点作为当前节点的后一节点,是为反转

        #指针后移
        $pre = $cur;    
        $cur = $next;   
    }
    return $pre;
}
}
$object = new SingleLinkList();
$result = (new SingleLinkList)->headInsert(4);
$pre = $object->ReverseList($result);
//$behind = $object->FindKthToTail($result,1);

// $e = $object->readIThNode($result,2);
// echo $e;

// $newNode = new Node(6,null);
// $newList = $object->Insert($result,2,$newNode);

// $e = $object->Delete($result,2);

 echo "
";
// print_r($result);
print_r($pre);

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/25965.html

相关文章

  • 利用PHP实现《剑指 offer》链表数据结构与算法实战 )

    摘要:一定要认真看分析注释题目要求题目描述输入一个链表,从尾到头打印链表每个节点的值。分析因为链表只有知道当前结点才能知道下一结点,所以不可能直接从后往前打印。 一定要认真看 分析 | 注释 | 题目要求 Question 1 题目描述:输入一个链表,从尾到头打印链表每个节点的值。 分析:因为链表只有知道当前结点才能知道下一结点,所以不可能直接从后往前打印。这种逆序的算法(策略)我们常用栈这...

    hiYoHoo 评论0 收藏0
  • 学习数据结构与算法链表

    摘要:本系列所有文章第一篇文章学习数据结构与算法之栈与队列第二篇文章学习数据结构与算法之链表第三篇文章学习数据结构与算法之集合第四篇文章学习数据结构与算法之字典和散列表第五篇文章学习数据结构与算法之二叉搜索树简单介绍链表链表一种常见的数据结构,可 本系列所有文章:第一篇文章:学习数据结构与算法之栈与队列第二篇文章:学习数据结构与算法之链表第三篇文章:学习数据结构与算法之集合第四篇文章:学习数...

    jerryloveemily 评论0 收藏0
  • 利用PHP实现常用数据结构之二叉树(小白系列文章

    摘要:回来更新一波,最近刷剑指,才又发现树真是一个大头,二叉树的题目和变化运用好多啊二叉树算法引子很多人说二叉树没什么卵用,我觉得是他的工资和公司让他跨不过这个坎还有很多人学了一些树的知识,发现也用不上,我想说的是,读一本书体现不了这本书 回来更新一波,最近刷《剑指offer》,才又发现树真是一个大头,二叉树的题目和变化运用好多啊~ /** * PHP二叉树算法 * Create...

    developerworks 评论0 收藏0
  • PHPer也刷《剑指Offer》链表

    摘要:剑指中链表相关题目俗话说光说不练假把式,既然学习了链表的基础概念和基本操作那我们一定要找些题目巩固下,下面来看剑指中的相关题目。题目分析合并两个排序的链表,需要分别比较两个链表的每个值,然后改变指针。 温故知新 链表由一个一个的作为节点的对象构成的,每一个节点都有指向下一个节点的指针,最后一个节点的指针域指向空。每个节点可以存储任何数据类型。 根据类型可以分为单链表、双链表、环形链表、...

    daydream 评论0 收藏0
  • 利用PHP实现常用数据结构之二叉树(小白系列文章六)

    摘要:回来更新一波,最近刷剑指,才又发现树真是一个大头,二叉树的题目和变化运用好多啊二叉树算法引子很多人说二叉树没什么卵用,我觉得是他的工资和公司让他跨不过这个坎还有很多人学了一些树的知识,发现也用不上,我想说的是,读一本书体现不了这本书 回来更新一波,最近刷《剑指offer》,才又发现树真是一个大头,二叉树的题目和变化运用好多啊~ /** * PHP二叉树算法 * Create...

    Cympros 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<