程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> PHP無限極分類的實現(不使用遞歸),php遞歸

PHP無限極分類的實現(不使用遞歸),php遞歸

編輯:關於PHP編程

PHP無限極分類的實現(不使用遞歸),php遞歸


無限極分類在開發中經常使用,例如:部門結構、文章分類。無限極分類的難點在於“輸出”和“查詢”,例如

  • 將文章分類輸出為<ul>列表形式;
  • 查找分類A下面所有分類包含的文章。

1.實現原理

在《無限級分類實現思路》一文中介紹了幾種常見的實現方法,各有利弊。其中“改進前序遍歷樹”數據結構,便於輸出和查詢,但是在移動分類和常規理解上有些復雜。

2.數據結構

idfidtitle 1 0 中國 2 1 江蘇 3 1 安徽 4 8 江陰 5 3 蕪湖 6 3 合肥 7 3 蚌埠 8 2 無錫
<?php
    $list = array(
        array('id'=>1, 'fid'=>0, 'title' => '中國'), 
        array('id'=>2, 'fid'=>1, 'title' => '江蘇'),
        array('id'=>3, 'fid'=>1, 'title' => '安徽'),
        array('id'=>4, 'fid'=>8, 'title' => '江陰'),
        array('id'=>5, 'fid'=>3, 'title' => '蕪湖'),
        array('id'=>6, 'fid'=>3, 'title' => '合肥'),
        array('id'=>7, 'fid'=>3, 'title' => '蚌埠'),
        array('id'=>8, 'fid'=>8, 'title' => '無錫')
    );
?>

各分類之間通過父類id(即fid)進行級別“串聯”,形成一棵分類樹。在進行串聯時候有一點值得注意:分類A的fid不可以是其子類的id。

在使用這種數據結構進行輸出時最常用的算法就是“遞歸”,熟悉PHP語言的朋友肯定知道,PHP不擅長遞歸 ,而且遞歸次數有限(100次左右,因操作系統和配置而異)。

由於所有的遞歸均可以使用循環實現,本文根據PHP語言特點編寫了一套關於“無限級”分類的函數,相比遞歸實現而言效率更高

3.輸出ul列表形式

將上述數據輸出為下面的HTML

<ul>
    <li class="first-child">
        <div>江蘇</div>
        <ul>
            <li class="first-child last-child">
                <div>無錫</div>
                <ul>
                    <li class="first-child last-child">
                        <div>江陰</div>
                    </li>
                </ul>
            </li>
        </ul>
    </li>
    <li class="last-child">
        <div>安徽</div>
        <ul>
            <li class="first-child"><div>蕪湖</div></li>
            <li><div>合肥</div></li>
            <li class="last-child"><div>蚌埠</div></li>
        </ul>
    </li>
</ul>

這種HTML結構在前端使用(使用JavaScript和CSS構造可折疊樹)十分方便。具體實現程序如下:

<ul><?php get_tree_ul($list, 1); ?></ul>

4.輸出option列表形式

<select>
<option value="2">江蘇</option>
<option value="8">&nbsp;&nbsp;&nbsp;&nbsp;無錫</option>
<option value="4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;江陰</option>
<option value="3">安徽</option>
<option value="5">&nbsp;&nbsp;&nbsp;&nbsp;蕪湖</option>
<option value="6">&nbsp;&nbsp;&nbsp;&nbsp;合肥</option>
<option value="7">&nbsp;&nbsp;&nbsp;&nbsp;蚌埠</option>
</select>

具體實現程序如下:

<select>
<?php
    // get_tree_option()返回數組,並為每個元素增加了“深度”(即depth)列,直接輸出即可
    $options = get_tree_option($list, 1); 
    foreach($options as $op) {
        echo '<option value="' . $op['id'] .'">' . str_repeat(" ", $op['depth'] * 4) . $op['title'] . '<;/option>';
    }
?>
<;/select>

5. 查找某一分類的所有子類

<?php
    $children = get_tree_child($list, 0);
    echo implode(',', $children);    // 輸出:1,3,2,7,6,5,8,4
?>

6. 查找某一分類的所有父類

<?php
    $children = get_tree_parent($list, 4);
    echo implode(',', $children);    //8, 2, 10
?>

7. 相關函數

<?php
function get_tree_child($data, $fid) {
    $result = array();
    $fids = array($fid);
    do {
        $cids = array();
        $flag = false;
        foreach($fids as $fid) {
            for($i = count($data) - 1; $i >=0 ; $i--) {
                $node = $data[$i];
                if($node['fid'] == $fid) {
                    array_splice($data, $i , 1);
                    $result[] = $node['id'];
                    $cids[] = $node['id'];
                    $flag = true;
                }
            }
        }
        $fids = $cids;
    } while($flag === true);
    return $result;
}

function get_tree_parent($data, $id) {
    $result = array();
    $obj = array();
    foreach($data as $node) {
        $obj[$node['id']] = $node;
    }    

    $value = isset($obj[$id]) ? $obj[$id] : null;    
    while($value) {
        $id = null;
        foreach($data as $node) {
            if($node['id'] == $value['fid']) {
                $id = $node['id'];
                $result[] = $node['id'];
                break;
            }
        }
        if($id === null) {
            $result[] = $value['fid'];
        }
        $value = isset($obj[$id]) ? $obj[$id] : null;
    }
    unset($obj);
    return $result;
}

function get_tree_ul($data, $fid) {
    $stack = array($fid);
    $child = array();
    $added_left = array();
    $added_right= array();
    $html_left     = array();
    $html_right    = array();
    $obj = array();
    $loop = 0;
    foreach($data as $node) {
        $pid = $node['fid'];
        if(!isset($child[$pid])) {
            $child[$pid] = array();
        }
        array_push($child[$pid], $node['id']);
        $obj[$node['id']] = $node;
    }

    while (count($stack) > 0) {    
        $id = $stack[0];
        $flag = false;
        $node = isset($obj[$id]) ? $obj[$id] : null;
        if (isset($child[$id])) {
            $cids = $child[$id];
            $length = count($cids);
            for($i = $length - 1; $i >= 0; $i--) {
                array_unshift($stack, $cids[$i]);
            }
            $obj[$cids[$length - 1]]['isLastChild'] = true;
            $obj[$cids[0]]['isFirstChild'] = true;
            $flag = true;
        }
        if ($id != $fid && $node && !isset($added_left[$id])) {
            if(isset($node['isFirstChild']) && isset($node['isLastChild']))  {
                $html_left[] = '<li class="first-child last-child">';
            } else if(isset($node['isFirstChild'])) {
                $html_left[] = '<li class="first-child">';
            } else if(isset($node['isLastChild'])) {
                $html_left[] = '<li class="last-child">';
            } else {
                $html_left[] = '<li>';
            }            
            $html_left[] = ($flag === true) ? "<div>{$node['title']}</div><ul>" : "<div>{$node['title']}</div>";
            $added_left[$id] = true;
        }    
        if ($id != $fid && $node && !isset($added_right[$id])) {
            $html_right[] = ($flag === true) ? '</ul></li>' : '</li>';
            $added_right[$id] = true;
        }

        if ($flag == false) {
            if($node) {
                $cids = $child[$node['fid']];
                for ($i = count($cids) - 1; $i >= 0; $i--) {
                    if ($cids[$i] == $id) {
                        array_splice($child[$node['fid']], $i, 1);
                        break;
                    }
                } 
                if(count($child[$node['fid']]) == 0) {
                    $child[$node['fid']] = null;
                }
            }
            array_push($html_left, array_pop($html_right));
            array_shift($stack);
        }
        $loop++;
        if($loop > 5000) return $html_left;
    }
    unset($child);
    unset($obj);
    return implode('', $html_left);
}

function get_tree_option($data, $fid) {
    $stack = array($fid);
    $child = array();
    $added = array();
    $options = array();
    $obj = array();
    $loop = 0;
    $depth = -1;
    foreach($data as $node) {
        $pid = $node['fid'];
        if(!isset($child[$pid])) {
            $child[$pid] = array();
        }
        array_push($child[$pid], $node['id']);
        $obj[$node['id']] = $node;
    }

    while (count($stack) > 0) {    
        $id = $stack[0];
        $flag = false;
        $node = isset($obj[$id]) ? $obj[$id] : null;
        if (isset($child[$id])) {
            for($i = count($child[$id]) - 1; $i >= 0; $i--) {
                array_unshift($stack, $child[$id][$i]);
            }
            $flag = true;
        }
        if ($id != $fid && $node && !isset($added[$id])) {
            $node['depth'] = $depth;
            $options[] = $node;
            $added[$id] = true;
        }
        if($flag == true){
            $depth++;
        } else {
            if($node) {
                for ($i = count($child[$node['fid']]) - 1; $i >= 0; $i--) {
                    if ($child[$node['fid']][$i] == $id) {
                        array_splice($child[$node['fid']], $i, 1);
                        break;
                    }
                } 
                if(count($child[$node['fid']]) == 0) {
                    $child[$node['fid']] = null;
                    $depth--;
                }
            }
            array_shift($stack);
        }
        $loop++;
        if($loop > 5000) return $options;
    }
    unset($child);
    unset($obj);
    return $options;
}
?>

php遞歸問題(無限極分類)

foreach本身就相當於判斷了啊,當$arr數組非空時foreach會遍歷並遞歸訪問子節點,但是對於葉子結點,$arr數組是空的,根本不會被foreach,此時直接return了。明白?
 

php 遞歸問題,想要將數據按照無限極分類的樣式顯示出來

首先 將json 解碼成數組, 用json_decode 函數 注意 一定要加上第二個參數 否則他會返回一個對象。接下來就是地遞歸了。這是一個最簡單的遞歸只要逐個遍歷即可。
下面是完整的代碼:
$data= json_decode($str,true);$options = getChildren($data);function getChildren($parent,$deep=0) {foreach($parent as $row) {$data[] = array("id"=>$row['id'], "name"=>$row['name'],"pid"=>$row['parentid'],'deep'=>$deep);if ($row['childs']) {$data = array_merge($data, getChildren($row['childs'], $deep+1));}}return $data;}?><select name="" id=""><?php foreach ($options as $row) { ?><option value="<?php echo $row['id'] ?>"><?php echo str_pad("",$row['deep']*3, "-",STR_PAD_RIGHT); ?><?php echo $row['name']; ?></option><?php } ?></select>
以上代碼已測試通過 效果圖如下



 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved