PHP 鏈表的使用

前面已經(jīng)介紹了很多關(guān)于數(shù)組的內(nèi)容.下面我們來(lái)討論一下鏈表.在多數(shù)語(yǔ)言中,數(shù)組是定長(zhǎng)的結(jié)構(gòu),當(dāng)然就不能動(dòng)態(tài)增長(zhǎng),縮減和移除.出于這些原因,大多數(shù)開(kāi)發(fā)者都偏向于使用鏈表來(lái)代替數(shù)組.考慮到PHP數(shù)組中有額外的字節(jié)消耗,而鏈表的對(duì)內(nèi)存使用的效率很高.下面將介紹PHP中不同類型的鏈表以及實(shí)現(xiàn).

鏈表是什么?

鏈表是節(jié)點(diǎn)的集合.每一個(gè)節(jié)點(diǎn)通過(guò)鏈表相互連接.節(jié)點(diǎn)可以存儲(chǔ)任何數(shù)據(jù)結(jié)構(gòu)和內(nèi)容.

鏈表有以下操作:

  • 鏈表是否為空
  • 遍歷所有節(jié)點(diǎn)
  • 搜索某一節(jié)點(diǎn)
  • 求鏈表長(zhǎng)度
  • 插入新節(jié)點(diǎn)
  • 移除節(jié)點(diǎn)
  • 鏈表的反轉(zhuǎn)

下面描述一種簡(jiǎn)單的節(jié)點(diǎn),包含節(jié)點(diǎn)數(shù)據(jù)和下一個(gè)節(jié)點(diǎn)的地址
next:

class ListNode { 
    public $data = NULL; 
    public $next = NULL; 

    public function __construct(string $data = NULL) { 
        $this->data = $data; 
    } 
}

下面看一個(gè) 鏈表 LinkedList 的實(shí)現(xiàn) ,包含insert和display操作

class LinkedList { 
    private $_firstNode = NULL; 
    private $_totalNodes = 0; 

    public function insert(string $data = NULL) { 
       $newNode = new ListNode($data); 

        if ($this->_firstNode === NULL) {           
            $this->_firstNode = &$newNode;             
        } else { 
            $currentNode = $this->_firstNode; 
            while ($currentNode->next !== NULL) { 
                $currentNode = $currentNode->next; 
            } 
            $currentNode->next = $newNode; 
        } 
       $this->_totalNode++; 
        return TRUE; 
    } 

    public function display() { 
      echo "Total book titles: ".$this->_totalNode."\n"; 
        $currentNode = $this->_firstNode; 
        while ($currentNode !== NULL) { 
            echo $currentNode->data . "\n"; 
            $currentNode = $currentNode->next; 
        } 
    } 
} 

$linked_list = new LinkedList();

$linked_list->insert(1);
$linked_list->insert(2);
$linked_list->insert(3);
$linked_list->insert(4);

$linked_list->display();

返回結(jié)果:
Total book titles: 4 
1 2 3 4

不同類型的鏈表

到此我們已經(jīng)實(shí)現(xiàn)了一種最簡(jiǎn)單的單向鏈表類 LinkedList 這里還有其他幾中鏈表

  • 雙向鏈表
  • 環(huán)向列表
  • 多鏈表
雙向列表
雙向鏈表
環(huán)向列表
環(huán)向列表

多鏈表

多鏈表

鏈表的插入,刪除,查找

前面了解了鏈表的插入和遍歷操作,下面來(lái)看一下鏈表的其他操作

  • 在第一個(gè)節(jié)點(diǎn)處插入
  • 節(jié)點(diǎn)的搜索
  • 在特殊節(jié)點(diǎn)前插入
  • 在特殊節(jié)點(diǎn)后插入
  • 刪除第一個(gè)節(jié)點(diǎn)
  • 刪除最后一個(gè)節(jié)點(diǎn)
  • 搜索和刪除一個(gè)節(jié)點(diǎn)
  • 鏈表的反轉(zhuǎn)
  • 獲取第N個(gè)位置的元素

下面依次討論

在第一個(gè)節(jié)點(diǎn)處插入

節(jié)點(diǎn)搜索

   public function insertAtFirst(string $data = NULL) { 
        $newNode = new ListNode($data); 
        if ($this->_firstNode === NULL) {             
            $this->_firstNode = &$newNode;             
        } else { 
            $currentFirstNode = $this->_firstNode; 
            $this->_firstNode = &$newNode; 
            $newNode->next = $currentFirstNode;            
        } 
        $this->_totalNode++; 
        return TRUE; 
    } 

在特殊節(jié)點(diǎn)前插入

public function insertBefore(string $data = NULL, string $query =NULL)
    {   
        $newNode = new ListNode($data);
        if ($this->_firstNode && $this->_firstNode->data = $query) {
            $newNode->next = $this->_firstNode;
            $this->_firstNode-> &$newNode;
        }

        if ($this->_firstNode !== NULL) {       
            $previous = NULL;
            $currentNode = $this->_firstNode;
            while ($currentNode !== NULL) {
                if ($currentNode->data === $query) {
                    $previous->next = &$newNode;
                    $newNode->next = $currentNode;
                    $this->_totalNode++;
                    break;
                }
                $previous = $currentNode;
                $currentNode = $currentNode->next;
            }
        }
    }

在特定節(jié)點(diǎn)后插入

    public function insertAfter(string $data = NULL, string $query =NULL)
    {   
        $newNode = new ListNode($data);
        if ($this->_firstNode !== NULL) {
            $currentNode = $this->_firstNode;
            while ($currentNode !== NULL) {
                if ($currentNode->data === $query) {
                    $newNode->next = $currentNode->next;
                    $currentNode->next = &$newNode;
                    $this->_totalNode++;
                    break;
                }
                $currentNode = $currentNode->next;
            }
        }
    }

鏈表的反轉(zhuǎn)

    public function reverse() { 
        if ($this->_firstNode !== NULL) { 
            if ($this->_firstNode->next !== NULL) { 
                $reversedList = NULL; 
                $next = NULL; 
                $currentNode = $this->_firstNode; 
                while ($currentNode !== NULL) { 
                    $next = $currentNode->next; 
                    $currentNode->next = $reversedList; 
                    $reversedList = $currentNode; 
                    $currentNode = $next; 
                } 
                $this->_firstNode = $reversedList; 
            } 
        } 
    } 

獲取第N個(gè)位置的元素

    public function getNthNode(int $n = 0)
    {
        $count = 1;
        if ($this->_firstNode !== NULL) {
            $currentNode = $this->_firstNode;
            while ($currentNode !== NULL) {
                if ($count === $n) {
                    return $currentNode;
                }
                $count++;
                $currentNode = $currentNode->next;
            }
        }
    }

理解鏈表的算法復(fù)雜度

操作 時(shí)間復(fù)雜度:最差 事件復(fù)雜度:平均
在首,尾插入 O(1) O(1)
在首尾刪除 O(1) O(1)
搜索 O(n) O(n)
訪問(wèn) O(n) O(n)

使鏈表可迭代

上面我們使用 while 遍歷鏈表的每一個(gè)節(jié)點(diǎn).那我們可以通過(guò)鏈表本身進(jìn)行迭代嗎?PHP 有非常直觀的 Iterator(迭代器) 接口

  • Current
  • Next
  • Key
  • Rewind
  • Valid

上面的方法可以通過(guò) php 手冊(cè)查詢,不再贅述.下面我們將通過(guò)類 LinkedList 來(lái)實(shí)現(xiàn)上述接口來(lái)實(shí)現(xiàn)直接進(jìn)行節(jié)點(diǎn)的迭代.為了在迭代的過(guò)程中追蹤當(dāng)前節(jié)點(diǎn)和當(dāng)前的位置,類 LinkedList 需要額外的兩個(gè)屬性

private $_currentNode = NULL;  // 當(dāng)前節(jié)點(diǎn)
private $_currentPosition = 0; // 當(dāng)前位置

下面實(shí)現(xiàn) Iterator(迭代器)的5個(gè)方法

    #pragram make implements Iterator
    public function current()
    {
        return $this->_currentNode->data;
    }

    public function next()
    {
        $this->_currentPosition++;
        $this->_currentNode = $this->_currentNode->next;
    }    

    public function key() { 
        return $this->_currentPosition; 
    } 

    public function rewind() { 
        $this->_currentPosition = 0; 
        $this->_currentNode = $this->_firstNode; 
    } 

    public function valid() { 
        return $this->_currentNode !== NULL; 
    } 

哈哈,現(xiàn)在我們有了列表的迭代方法了,意味著我們可以使用 foreach 或者其他過(guò)程函數(shù)來(lái)進(jìn)行迭代:例如下面:

foreach ($BookTitles as $title) { 
    echo $title . "\n"; 
}
for ($BookTitles->rewind(); $BookTitles->valid(); $BookTitles->next()) { 
    echo $BookTitles->current() . "\n"; 
}

雙向列表

后面雙向列表和多向列表的內(nèi)容和算法我覺(jué)得不需要在贅述了,有空在寫(xiě)吧

使用 PHP SplDoublyLinkedList

如果我們使用內(nèi)置的類,我們不需要去實(shí)現(xiàn)雙向列表.官方手冊(cè)中清楚的列出了雙向列表的接口,這里也不再介紹了,下面就舉個(gè)例子:







?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挟鸠,一起剝皮案震驚了整個(gè)濱河市预皇,隨后出現(xiàn)的幾起案子粪滤,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仇冯,死亡現(xiàn)場(chǎng)離奇詭異进萄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)磁玉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)停忿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蚊伞,你說(shuō)我怎么就攤上這事席赂。” “怎么了时迫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵颅停,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我掠拳,道長(zhǎng)癞揉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮喊熟,結(jié)果婚禮上柏肪,老公的妹妹穿的比我還像新娘。我一直安慰自己逊移,他們只是感情好预吆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著胳泉,像睡著了一般拐叉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扇商,一...
    開(kāi)封第一講書(shū)人閱讀 49,929評(píng)論 1 290
  • 那天凤瘦,我揣著相機(jī)與錄音,去河邊找鬼案铺。 笑死蔬芥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的控汉。 我是一名探鬼主播笔诵,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼姑子!你這毒婦竟也來(lái)了乎婿?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤街佑,失蹤者是張志新(化名)和其女友劉穎谢翎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體沐旨,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡森逮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磁携。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褒侧。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖谊迄,靈堂內(nèi)的尸體忽然破棺而出闷供,到底是詐尸還是另有隱情,我是刑警寧澤鳞上,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布这吻,位于F島的核電站吊档,受9級(jí)特大地震影響篙议,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一鬼贱、第九天 我趴在偏房一處隱蔽的房頂上張望移怯。 院中可真熱鬧,春花似錦这难、人聲如沸舟误。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嵌溢。三九已至,卻和暖如春蹋岩,著一層夾襖步出監(jiān)牢的瞬間赖草,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工剪个, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秧骑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓扣囊,卻偏偏與公主長(zhǎng)得像乎折,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子侵歇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容