利用PHP實現《劍指 offer》之連結串列(資料結構與演算法實戰 )

entner發表於2019-02-16

一定要認真看 分析 | 註釋 | 題目要求

Question 1

題目描述:
輸入一個連結串列,從尾到頭列印連結串列每個節點的值。

分析:
因為連結串列只有知道當前結點才能知道下一結點,所以不可能直接從後往前列印。這種逆序的演算法(策略)我們常用這種資料結構來解決,所以我們的基本思路為,先將連結串列壓入棧,再彈出,但是這樣做我們需要遍歷兩次才能得出答案,更奇妙的解決方案為一次將結點值插入陣列頭部,一次便可以滿足題目要求,程式碼如下:

<?php

/*class ListNode{
    var $val;
    var $next = NULL;
    function __construct($x){
        $this->val = $x;
    }
}*/

function printListFromTailToHead($head)
{
    $stack = [];
    if(!$head){
        return $stack;
    }
    while($head){
        array_unshift($stack,$head->val);   #array_unshift返回頭插後的陣列單元數目
        $head = $head->next;
    }
    return $stack;
}

測試地址:https://www.nowcoder.com/prac…

Question 2

題目描述
輸入一個連結串列,輸出該連結串列中倒數第k個結點。

分析:
前提:連結串列是動態分配的,事先不能知道連結串列的總長度
一般思路:遍歷連結串列,得出長度,輸出結點
面試思路:準備兩個指標,假如第一個指標走到n-1(即連結串列末尾),第二個指標走到倒數k的位置,兩者之間相差(n-1)-(n-k) = k-1,先讓一個指標走到k-1,第二個指標原地等待,然後讓第二個指標和第一個指標同時走,走到末尾,找到k,程式碼如下:

<?php


    /**
     * Question1:輸出倒數第K個節點
     * @param $head object 連結串列
     * @param $k     int    序號
     * Think: 動態分配不屬於固定空間 不知道連結串列實際有
     */
    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個單位,如果不為空,則指標向後移動    */
            if($head->next != null){
                $head = $head->next;
            }else{
                 /*    注意:這裡有一個隱藏的條件,就是連結串列的長度有可能小於k,當我們走到k-1時連結串列就已經遍歷結束,這種情況同樣非法    */
                return null;
            }
        }
        /*    當第一個指標走到k-1且還不為空,這時讓第二個指標開始走,當第一個指標走到n-1的時候,第二個指標也走到了倒數第k的位置,即所求    */
        while($head->next != null){
            $head = $head->next;
            $behind = $behind->next;
        }
        return $behind;
    }

測試地址:https://www.nowcoder.com/prac…

Question 3

題目描述
輸入一個連結串列,反轉連結串列後,輸出連結串列的所有元素。

分析:
畫圖最佳 主要就是運用臨時節點 看註釋

<?php


    /**
     * Question:輸入一個連結串列,反轉連結串列後,輸出連結串列的所有元素
     * @param $pHead object 連結串列
     * Think: 畫圖最佳 主要就是運用臨時節點
     */
    function ReverseList($pHead){

        if($pHead == NULL){
            return NULL;
        }

        $cur = null;

        while($pHead){
            $tmp = $pHead->next;    #首先將鏈上第二個位置的值放在臨時容器中
            $pHead->next = $cur;    #將第二個位置賦一個空值 保持鏈的關係

            $cur = $pHead;          #將第一個位置的值賦給第二個位置
            $pHead = $tmp;          #將原第二個位置的值賦給頭結點
        }
        return $cur;
    }

測試地址:https://www.nowcoder.com/prac…

Question 4

題目描述
輸入兩個單調遞增的連結串列,輸出兩個連結串列合成後的連結串列,當然我們需要合成後的連結串列滿足單調不減規則。

分析:
畫圖最佳 先用頭結點比較大小,小的壓入陣列,大的和小的的後一位繼續比較

<?php
/*class ListNode{
    var $val;
    var $next = NULL;
    function __construct($x){
        $this->val = $x;
    }
}*/
function MergeList($pHead1, $pHead2)
{
    // write code here
    /*
         先用頭結點比較大小,小的壓入陣列,大的和小的的後一位繼續比較
    */
    if($pHead1 == null && $pHead2 == null){
        return null;
    }
    if($pHead1 == null){
        return $pHead2;
    }
    if($pHead2 == null){
        return $pHead1;
    }

    $target = array();

    if($pHead1 != null && $pHead2 != null){     #這裡作判斷的目的主要是在遞迴過程中可能會有一方先遍歷結束
        if($pHead1->val >= $pHead2->val){
            $target = $pHead2;
            $target->next = MergeList($pHead1,$pHead2->next);
        }else{
            $target = $pHead1;
            $target->next = MergeList($pHead1->next,$pHead2);
        }
    }
    return $target;
    
}

測試地址:https://www.nowcoder.com/prac…

Question 5

題目描述
給定一個連結串列

1.判斷連結串列是否有環
2.連結串列起始結點(入口結點)

分析:
最近segment插圖好像出了點問題,我給個連結吧環形連結串列

function EntryNodeOfLoop($pHead)
{
    // write code here
    if($pHead == null){
        return null;
    }
    $fast = $pHead;
    $slow = $pHead;
    
    while($fast != null && $fast->next != null){
        $fast = $fast->next->next;
        $slow = $slow->next;
        if($fast == $slow){  #存在環
            $slow = $pHead;
            while($fast != $slow){ #此時slow為頭結點
                $fast = $fast->next;
                $slow = $slow->next;
            }
            if($fast == $slow){
                    return $fast;
                }
   
        }
    }
   
    return null;
}

Question 6

題目描述
“約瑟夫環”是一個數學的應用問題:一群猴子排成一圈,按1,2,…,n依次編號。然後從第1只開始數,數到第m只,把它踢出圈,從它後面再開始數, 再數到第m只,在把它踢出去…,如此不停的進行下去, 直到最後只剩下一隻猴子為止,那隻猴子就叫做大王。要求程式設計模擬此過程,輸入m、n, 輸出最後那個大王的編號。

分析:
第一個出列的猴子肯定是a[1]=m(mod)n,設此時某個猴子的新編號是i,他原來的編號就是(i+a[1])%n

/**
* @param  $monkeys Array 預設陣列
* @param  $m.    Int。   預設剔除序號
* @param  $cur.  Int。   標記
*/
function JosephCircle($monkeys , $m , $current = 0){
    $number = count($monkeys);
    $num = 1;
    if(count($monkeys) == 1){
        echo $monkeys[0];
        return;
    }
    else{
        while($num++ < $m){
            $current++ ;
            $current = $current%$number;
        }
        echo $monkeys[$current];
        array_splice($monkeys , $current , 1);  #剔除
        JosephCircle($monkeys , $m , $current);
    }
}

相關文章