遞歸算法形成的問題分析與解決

原文是在我本身博客中,小夥伴也能夠點 閱讀原文進行跳轉查看,還有好聽的背景音樂噢~

    遞歸,在編碼中應該算是一種很常見的算法了。以前在學習C語言的時候,也一樣瞭解過一些基本的算法,好比斐波那契。在學習的時候,對算法這種編程技巧就有了一種濃濃的敬畏之心,由於以爲會算法的人就很厲害了,能夠把很長的代碼塊經過一段簡短的算法解決並獲得想要的結果。php

今天在實際工做中也遇到了算法中一些問題。整理一下,造成今天的內容【算法中的遞歸算法】。html

什麼是遞歸

借用百科的一段話來表述就是:算法

一個過程或函數在其定義或說明中有直接或間接調用自身的一種方法。

遞歸的能力

一樣引用百科的一句話,我的以爲很是經典:編程

用有限的語句來定義對象的無限集合;

這句話什麼意思呢,通俗點來理解就是,我程序只有一套,可是我能夠經過遞歸(自身調用自身)的特性,無論你有多少個值,我都能妥妥的給你按照特定的程序邏輯處理嘍。(就是這麼強勢,嘿嘿!)segmentfault

本身以前對遞歸的理解就是本身調用本身,經過屢次的本身調用自身,經過同一套程序方法,來達到解決問題的目的;這種方法能夠明顯的減小代碼量,並且靈活,尤爲是在多重循環的時候,能夠採用遞歸來替代。可是這種方法也有缺點,就是增長了程序了運行速度,並且有時候可能會由於編碼不當,形成死循環、棧溢出等問題。可是隻要用好,解決問題仍是不差的;數組

問題所在

今天在工做中,遇到一個把無限分類的多維數組轉換成html樹的時候,就遇到了點小麻煩,多是由於一時馬虎,當局者迷的緣故,本身就像掉進死循環裏,一直出不來,後來,也是在請教身邊的朋友後,才獲得解決,下面咱們來看一下出現了什麼問題(其實問題已經提在了SF社區上,問題標題是多維數組分類樹 組合html樹的問題?(遞歸),有興趣的小夥伴能夠去看下):函數

數組結構

最初的數組結構是一個無限分類的多維數組:學習

數組結構

由上圖能夠看到,這個數組的childs下標裏面對應的就是子分類,分類能夠有無限個。咱們要把它組裝成下圖的理想形態:測試

理想輸出

雖然看着很簡單,可是實際上走了很多彎路,最後卡在了一個點上,始終沒出來。我最開始的遞歸方法是:編碼

問題代碼

function creatHtmlTree($tree)
{
    // 生命一個靜態變量
    static $htmlTree;
    $htmlTree .= '<ul>';
    foreach ($tree as $key => $value) {
        $htmlTree .= "<li><span><i class='icon-folder-open'></i>{$value['name']} </span> <a href=''>Goes somewhere</a>";
        if (isset($value['childs']) && is_array($value['childs'])) {
            // 每次的結果累加到靜態變量上
            $html = creatHtmlTree($value['childs']);
            $htmlTree .= $html;
        } 
        $htmlTree .= "</li>";
    }
    $htmlTree .= "</ul>";
    return $htmlTree;
}

經過測試獲得了下圖的錯誤內容:

錯誤輸出

問題分析

咱們能夠看到,它給$htmlTree這個變量給了多餘的值,經過求教才明白,個人代碼中

static $htmlTree;
$htmlTree .= '<ul>';

以及if裏的

$html = creatHtmlTree($value['childs']);
$htmlTree .= $html;

代碼邏輯寫的有問題,問題在於,既然設定了$htmlTree爲靜態變量,那麼在遞歸中的每一次計算中,都默認已經$htmlTree賦予了最後的計算結果,我在if裏又把結果加了一次,因此才形成了輸出出現問題的狀況,那麼如何改爲呢?只需把:

$html = creatHtmlTree($value['childs']);
$htmlTree .= $html;

改成:

creatHtmlTree($value['childs']);

便可。這樣,他在遞歸運算的時候就能夠經過

$htmlTree .= '<ul>';

$htmlTree .= "<li><span><i class='icon-folder-open'></i>{$value['name']} </span> <a href=''>Goes somewhere</a>";

$htmlTree .= "</li>";

$htmlTree .= "</ul>";

這四行代碼來給$htmlTree累加數值就能夠了。

解決

來看一下,最終形態的遞歸方法是什麼樣子:

// 遞歸運算建立html樹結構
function creatHtmlTree($tree)
{
    // 聲明靜態變量
    static $htmlTree;
    $htmlTree .= '<ul>';
    foreach ($tree as $key => $value) {
        // 給靜態$htmlTree變量累加值
        $htmlTree .= "<li><span><i class='icon-folder-open'></i>{$value['name']} </span> <a href=''>Goes somewhere</a>";
        if (isset($value['childs']) && is_array($value['childs'])) {
            creatHtmlTree($value['childs']);
        } 
        $htmlTree .= "</li>";
    }
    // 賦值ul閉合標籤
    $htmlTree .= "</ul>";
    return $htmlTree;
}

這樣就能夠解決了。一樣還有另一種方式,那就是經過返回值的方式,來進行遞歸運算:

// 遞歸運算建立html樹結構
function creatHtmlTree($tree)
{
    // $htmlTree爲普通局部變量;
    $htmlTree .= '<ul>';
    
    foreach ($tree as $key => $value) {
        // 給變量$htmlTree累加值
        $htmlTree .= "<li><span><i class='icon-folder-open'></i>{$value['name']} </span> <a href=''>Goes somewhere</a>";
        if (isset($value['childs']) && is_array($value['childs'])) {
            // 遞歸中每次的結果累加到$htmlTree
            $htmlTree .= creatHtmlTree($value['childs']);
        } 
        $htmlTree .= "</li>";
    }
    // 賦值ul閉合標籤
    $htmlTree .= "</ul>";
    return $htmlTree;
}

經過這種返回值累加的算法,也一樣能夠獲得想要的結果。

測試

今天爲了測試和解決遞歸算法帶來的問題,特地找了段代碼進行測試,也是我下午一直在實驗的demo,手癢癢的小夥伴,能夠立馬copy到本地親自體驗一下:

<?php 

$data = [
    ['id'=>1,'parentid'=>0,'name'=>'中國'],
    ['id'=>2,'parentid'=>0,'name'=>'美國'],
    ['id'=>3,'parentid'=>0,'name'=>'韓國'],
    ['id'=>4,'parentid'=>1,'name'=>'北京'],
    ['id'=>5,'parentid'=>1,'name'=>'上海'],
    ['id'=>6,'parentid'=>1,'name'=>'廣西'],
    ['id'=>7,'parentid'=>6,'name'=>'桂林'],
    ['id'=>8,'parentid'=>6,'name'=>'南寧'],
    ['id'=>9,'parentid'=>6,'name'=>'柳州'],
    ['id'=>10,'parentid'=>2,'name'=>'紐約'],
    ['id'=>11,'parentid'=>2,'name'=>'華盛頓'],
    ['id'=>12,'parentid'=>3,'name'=>'首爾'],
];


 /**格式化數組輸出**/
function p($arr)
{
    echo "<pre>";
    echo '========================開始========================';
    echo "</br>";
    if( $arr ){
        print_r($arr);
    } else {
        echo '此值爲空';
    }
    echo "</br>";
    echo '========================結束========================';
    echo "</pre>";
}

/**
 * 多維數組樹形結構
 */
function tree($data, $pid = 0)
{
    $children = [];
    foreach ($data as $key => $value) {

        if ($value['parentid'] == $pid) {
            $children[] = $value;
        }
    }
    if (empty($children)) {
        return null;
    }

    foreach ($children as $key => $value) {
        $chid = tree($data, $value['id']);
        if ($chid != null) {
            $children[$key]['childs'] = $chid;
        }
    }

    return $children;
}

// 遞歸運算建立html樹結構
function creatHtmlTree($tree)
{
    // $htmlTree爲普通局部變量;
    $htmlTree .= '<ul>';
    
    foreach ($tree as $key => $value) {
        // 給$htmlTree變量累加值
        $htmlTree .= "<li><span><i class='icon-folder-open'></i>{$value['name']} </span> <a href=''>Goes somewhere</a>";
        if (isset($value['childs']) && is_array($value['childs'])) {
            // 遞歸中每次的結果累加到$htmlTree
            $htmlTree .= creatHtmlTree($value['childs']);
        } 
        $htmlTree .= "</li>";
    }
    // 賦值ul閉合標籤
    $htmlTree .= "</ul>";
    return $htmlTree;
}


$tree = tree($data);
$htmlTree = creatHtmlTree($tree);

p($tree);
p($htmlTree);

總結

算法,這門技巧,是我向往的高級玩意兒。以爲它挺炫的,在開頭我就有提到,能夠用極短的代碼解決複雜的業務程序,大大減小的代碼量。但它一樣也像一顆隱形炸彈同樣,也充滿着威脅。因此,在以後的遞歸算法中,應該當心謹慎,避免出現問題。

好了,今天就分享到這裏,以上。

補充

在百科裏看到遞歸解釋的兩句話,也一樣經典,奉上:

  1. 遞歸須要有邊界條件、遞歸前進段和遞歸返回段
  2. 當邊界條件不知足時,遞歸前進;當邊界條件知足時,遞歸返回

這大概說的就是遞歸的運行條件吧。完。

相關文章
相關標籤/搜索