tips:由於涉及指針,咱們用引用來模擬,因此讀者應該有面向對象的知識貯備。
你能夠把鏈表簡單理解爲動態數組,它不須要一塊一塊的開闢空間,同時,你又要注意,它存在的主要意義或者說使用場景主要是」指針功能「,它可以指來指去,對一些應用特別是內存管理起到了關鍵做用。php
由於涉及內存,經常會有一些程序的邊界限制,須要programer擁有必定嚴密的邏輯去保證代碼的魯棒性和健壯性,因此這個知識點是面試的常考點。下面咱們看看PHP的單鏈表實現(附常考題目實現):面試
<?php /** * PHP 單鏈表 * author:entner * time :2017-8-14 * email :1185087164@qq.com */ /** * TODO:構建鏈表節點 */ Class Node{ public $data; public $next; public function __construct($val,$nex){ $this->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 "<pre>"; // print_r($result); print_r($pre);