畫江湖之資料結構【第一話:連結串列】雙向連結串列

Krisji發表於2019-03-19

1 雙連結串列

概括:雙向連結串列有兩個引用分別指向前後節點。優勢方便向前查詢資料。
file
請看上圖:data為資料,pre為指向前個節點的引用,next為指向後個節點的引用 。

完整程式碼塊

<?php

/**
 * 節點實現
 */
class Node
{
    /**
     * 資料元素
     * @var
     */
    public $item;

    /**
     * 前驅節點
     * @var
     */
    public $prev;

    /**
     * 後繼節點
     * @var
     */
    public $next;

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

/**
 * 雙向連結串列基本操作
 *
 * 1.isEmpty()    連結串列是否為空
 * 2.length()      連結串列長度
 * 3.travel()      遍歷整個連結串列
 * 4.add(item)     連結串列頭部新增元素
 * 5.append(item)  連結串列尾部新增元素
 * 6.insert(pos, item) 指定位置新增元素
 * 7.remove(item)  刪除節點
 * 8.search(item)  查詢節點是否存在
 */
class DoubleLink
{
    /**
     * 頭節點
     * @var
     */
    private $head;

    /**
     * SingleLink constructor.
     */
    public function __construct()
    {
        $this->head = null;
    }

    /**
     * 連結串列是否為空
     * @return bool
     */
    public function isEmpty()
    {
        return is_null($this->head);
    }

    /**
     * 連結串列長度
     * @return int
     */
    public function length()
    {
        $cur = $this->head;
        $count = 0;
        while(!is_null($cur)){
            $count++;
            $cur = $cur->next;
        }
        return $count;
    }

    /**
     * 遍歷整個連結串列
     */
    public function travel()
    {
        $cur = $this->head;

        $tmp = [];

        while (!is_null($cur)) {
            array_push($tmp,$cur->item);
            $cur = $cur->next;
        }
        return $tmp;
    }

    /**
     * 連結串列頭部新增元素
     * @param $item
     */
    public function add($item)
    {
        $node = new Node($item);
        if($this->isEmpty()){
            $this->head = $node;
        }else{
            //待插入節點後繼節點為原本頭節點
            $node->next = $this->head;
            //待插入節點為原本頭節點的前驅節點
            $this->head->prev = $node;
            //待插入節點變為頭節點
            $this->head = $node;
        }
    }

    /**
     * 連結串列尾部新增元素
     * @param $item
     */
    public function append($item)
    {
        $node = new Node($item);
        if($this->isEmpty()){
            $this->head = $node;
        }else{
            //移動到尾節點
            $cur = $this->head;
            while (!is_null($cur->next)){
                $cur = $cur->next;
            }
            //原本尾節點next指向待插入節點
            $cur->next = $node;
            //待插入節點prev指向原本尾節點
            $node->prev = $cur;
        }
    }

    /**
     * 指定位置新增元素
     * @param $pos
     * @param $item
     */
    public function insert($pos, $item)
    {
        switch ($pos){
            //若指定位置pos為第一個元素之前,則執行頭部插入
            case $pos <= 0:
                $this->add($item);
                break;
            //若指定位置超過連結串列尾部,則執行尾部插入
            case $pos > ($this->length() - 1):
                $this->append($item);
                break;
            //找到位置
            default:
                $node = new Node($item);
                $count = 0;
                $cur = $this->head;

                //移到指定位置的前一個位置
                while ($count < ($pos - 1)){
                    $count++;
                    $cur = $cur->next;
                }
                $node->prev = $cur;
                $node->next = $cur->next;
                $cur->next->prev = $node;
                $cur->next = $node;
        }
    }

    /**
     * 刪除節點
     * @param $item
     */
    public function remove($item)
    {
        if($this->isEmpty()){
            return;
        }

        $cur = $this->head;
        //如果第一個就是刪除的節點
        if($cur->item == $item){
            //如果只有這一個節點
            if(is_null($cur->next)){
                $this->head = null;
            }else{
                $cur->next->prev = null;
                $this->head = $cur->next;
            }
            return;
        }
        while (!is_null($cur)){
            //找到元素
            if($cur->item == $item){
                $cur->prev->next = $cur->next;
                $cur->next->prev = $cur->prev;
                break;
            }
            $cur = $cur->next;
        }
    }

    /**
     * 查詢節點是否存在
     * @param $item
     * @return bool
     */
    public function search($item)
    {
        $cur = $this->head;
        while (!is_null($cur)){
            if($cur->item == $item){
                return true;
            }
            $cur = $cur->next;
        }
        return false;
    }
}

$s = new DoubleLink();
$s->add('23');
$s->add('er');
$s->add('33');
//$s->append('56');
//$s->insert(2,'2222');
//echo $s->length();
//var_dump($s->travel());
//var_dump($s->search('er'));
//$s->remove('er');
var_dump($s->travel());

2 分析程式碼塊 具體分析到程式碼註釋哦 ~

定義一個節點物件

/**
 * 節點實現
 */
class Node
{
    /**
     * 資料元素
     * @var
     */
    public $item;

    /**
     * 前驅節點
     * @var
     */
    public $prev;

    /**
     * 後繼節點
     * @var
     */
    public $next;

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

連結串列頭部新增元素 小夥伴們要注意哦 這個必須要執行三步驟

/**
     * 連結串列頭部新增元素
     * @param $item
     */
    public function add($item)
    {
        $node = new Node($item);//申明一個節點
        if($this->isEmpty()){//如果頭部節點為空的情況下
            $this->head = $node;//把頭部節點定義為當前定義的節點
        }
        //如果已經存在頭部節點的話
        else{
            //第一步:將插入的節點的下一個節點為頭部節點
            $node->next = $this->head;
            //第二步:將頭部的上一個節點為當前節點
            $this->head->prev = $node;
            //第三步:將頭部節點替換成當前節點
            $this->head = $node;
        }
    }
      /**
     * 連結串列是否為空
     * @return bool
     */
    public function isEmpty()
    {
        return is_null($this->head);
    }
    $s = new DoubleLink();
    $s->add('23');
    $s->add('er');
    $s->add('33');

連結串列尾部新增元素 小夥伴們要注意哦 看我畫的重點的步驟

/**
     * 連結串列尾部新增元素
     * @param $item
     */
    public function append($item)
    {
        $node = new Node($item);//定義一個節點物件
        if($this->isEmpty()){//如果頭部節點為空的情況下
            $this->head = $node;//把頭部節點定義為當前定義的節點
        }else{
            //移動到尾節點 開始 此次為重點
            $cur = $this->head;//定義一個頭部節點
            //這邊為了找到最後的尾部節點在哪裡 因為小哥哥要在尾部插入節點啊
            while (!is_null($cur->next)){//迴圈輸出下一個節點 不為空的情況下
                $cur = $cur->next;//將當前的節點定義為找到的尾部節點的下一個指標 因為是當前的節點
            }
            //移動到尾節點 結束

            //原本尾節點next指向待插入節點
            $cur->next = $node;//當前的下一個節點為要插入的節點
            //待插入節點prev指向原本尾節點
            $node->prev = $cur;//要插入的上一個節點為尾節點
        }
    }
    $s->append('56');

指定位置新增元素 小夥伴們要注意哦 看我畫的重點的步驟 四步驟狠關鍵

/**
     * 指定位置新增元素
     * @param $pos
     * @param $item
     */
    public function insert($pos, $item)
    {
        switch ($pos){
            //若指定位置pos為第一個元素之前,則執行頭部插入
            case $pos <= 0:
                $this->add($item);
                break;
            //若指定位置超過連結串列尾部,則執行尾部插入
            case $pos > ($this->length() - 1):
                $this->append($item);
                break;
            //找到位置 以上二點都不是的情況下怎麼辦呢
            default:
                $node = new Node($item);//先定義一個節點物件
                $count = 0;//定一個數量為0
                $cur = $this->head;//當前節點為頭部節點

                //移到指定位置的前一個位置
                while ($count < ($pos - 1)){ //這邊迴圈要移動到哪個位置上
                    $count++;
                    $cur = $cur->next;//把要插入到哪個位置上的節點 拿出來
                }
                //這裡很重要
                $node->prev = $cur;//第一步:要插入的節點上一個就是拿出來的當前節點
                $node->next = $cur->next;//第二步:要插入的節點的下一個節點 就是拿出來的當前的節點的下一個節點
                $cur->next->prev = $node;//第三步:這邊是關鍵步驟:把拿出來的下一個節點的上一個節點 指向要插入的節點
                $cur->next = $node;//第四步:把拿出來的下一個節點指向當前要插入的節點
        }
    }
$s->insert(2,'2222');

刪除節點: 小夥伴們要注意哦 這邊刪除的話看重點 刪除要處理前後節點的指標指向才能完全真正的刪除

/**
     * 刪除節點
     * @param $item
     */
    public function remove($item)
    {
        if($this->isEmpty()){//判斷頭部是否為空 如果為空就不用處理了
            return;
        }

        $cur = $this->head;//先定義一個當前元素 為頭部節點
        //如果第一個就是刪除的節點
        if($cur->item == $item){//如果當前節點的值為要刪除的值
            //如果只有這一個節點
            if(is_null($cur->next)){//如果這個節點的下一個節點為空的話
                $this->head = null;//直接把頭部節點為null
            }
            //如果頭部節點有下一個節點的話
            else{
                $cur->next->prev = null;//第一步:就把頭部節點的下一個節點的上一個節點為null
                $this->head = $cur->next;//第二步:就把頭部節點指向當前節點的下一個節點
            }
            return;
        }
        //如果要刪除的值不等於頭部節點的值的話 就要迴圈查詢要刪除的節點咯
        while (!is_null($cur)){
            //找到元素
            if($cur->item == $item){//查詢到了
                $cur->prev->next = $cur->next;//第一步:當前的上一個節點的下一個節點 替換為當前要刪除的節點的下一個節點
                $cur->next->prev = $cur->prev;//第二步:當前的下一個節點的上一個節點 替換為當前要刪除的節點的上一個節點
                break;
            }
            $cur = $cur->next;//定義當前的節點為下一個節點  作用迴圈使用
        }
    }
$s->remove('33');

查詢節點

/**
     * 查詢節點是否存在
     * @param $item
     * @return bool
     */
    public function search($item)
    {
        $cur = $this->head;//定義頭部節點
        while (!is_null($cur)){//迴圈節點
            if($cur->item == $item){//如果找到就找到了 很簡單吧

                return true;
            }
            $cur = $cur->next;
        }
        return false;
    }
    var_dump($s->search('er'));

迴圈節點

/**
     * 遍歷整個連結串列
     */
    public function travel()
    {
        $cur = $this->head;
        var_dump($cur);die();

        $tmp = [];//定義一個陣列

        while (!is_null($cur)) {//依次迴圈
            array_push($tmp,$cur->item);//把值放到陣列裡面
            $cur = $cur->next;
        }
        return $tmp;
    }

相關文章