1. 程式人生 > >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 echo 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無限分類實現評論及回覆

經常在各大論壇或新聞板塊詳情頁面下邊看到評論功能,當然不單單是直接發表評論內容那麼簡單,可以對別人的評論進行回覆,別人又可以對你的回覆再次評論或回覆,如此反覆,理論上可以說是沒有休止,從技術角度分析很容易想到運用無限級分類技術儲存資料,運用遞迴獲取評論層級結構

十進位制轉換二進位制C++實現

實現十進位制轉換為二進位制非遞迴實現 以下是C++原始碼: #include<iostream> #include<stdio.h> using namespace std; //十進位制 轉換為 二進位制------非遞迴 int DecToBin(int de

PHP無限分類實現使用

無限級分類在開發中經常使用,例如:部門結構、文章分類。無限級分類的難點在於“輸出”和“查詢”,例如 將文章分類輸出為<ul>列表形式; 查詢分類A下面所有分類包含的文章。 1.實現原理 在《無限級分類實現思路》一文中介紹了幾種常見的實現方法,各有利弊。其中“改進前序遍歷樹”資料結構

php實現無限分類牛逼

轉載路徑:http://www.php.cn/php-weizijiaocheng-353267.html 無限級樹狀圖可以說是無限級欄目的一個顯著特徵,我們接下來就來看看兩種不同的寫法。 一.資料庫設計 1 2 3

php 無限分類 +sort排序 和 非

1 先總結非遞迴 資料表: id name pid path 1 php 0 0 2 mysql 0 0 3 linux

PHP 無限分類資料庫設計及實現

♖背景 最近複習演算法,在此對無限級分類的實現方法稍作整理,當然也是參考了道友的經驗,目測適合實際的專案應用,當然,也有不少公司的筆試題還會涉及到呢,有何問題,歡迎各位道友指摘 … 操作環境:Wi

PHP無限分類技術——無限分類原理

第一章:遞迴無限分類原理 1.什麼是遞迴? 遞迴即程式呼叫自身的程式設計技巧稱為遞迴。 例如: Function getList($pid){ //定義一個函式 .......... if($row

MyBatis無限分類實現的兩種方法--自關聯與map集合

except app exce utf-8 elf findall ldr ati tex 1、這回先創建數據庫吧 下表cid是CategoryId的縮寫,cname是CategoryName的縮寫,pid是parentId的縮寫 無限級分類一般都包含這三個屬性,至少也要包

php無限分類實踐

1.先設計好資料庫新增好分類資料 具體設計可以看這個PHP無限級分類設計 可以隨便扒一個顯示多級分類的導航欄 2.控制器關鍵程式碼 先寫個三級分類的 多級就多次迴圈即可 public function index() { $type1=Db::n

無限分類實現思路 無限分類實現思路 (組織樹的分級管理)

無限級分類實現思路 (組織樹的分級管理) 關於該問題,暫時自己還沒有深入研究,在網上找到幾種解決方案,各有優缺點。 第一種方案: 使用遞迴演算法,也是使用頻率最多的,大部分開源程式也是這麼處理,不過一般都只用到四級分類。這種演算法的資料庫結構設計最為簡單。catego

java、js中實現無限層級的樹形結構類似

js中: var zNodes=[{id:0,pId:-1,name:"Aaaa"},     {id:1,pId:0,name:"A"},     {id:11,pId:1,name:"A1"},     {id:12,pId:1,name:"A2"},     {id

html移動應用 input 標籤 清除按鈕功能如何實現觸發鍵盤

有個需求是:輸入框有文字的時候就顯示清除按鈕,沒有文字則隱藏清除按鈕,點選清除按鈕不能影響鍵盤彈出的狀態。 網上有css實現自動顯示和隱藏清除按鈕的方案,但是考慮到相容性,我們還是使用js來實現。   css body{     &nbs

C語言 貪吃蛇實現閃屏

  不費話,直接上程式碼。VC++ 6.0可以完美執行,VS2015等版本需要做點修改,請按提示將“scanf”這些函式改成“_scanf”這樣的形式即可。   不閃屏的原理部分請參考這篇文章   程式碼只有兩百多行,很多地方都加上了註釋,有疑問可以

部分函式的與迭代實現

部分函式的遞迴和迭代(非遞迴)實現 首先,說到遞迴函式,大家一般都會想到斐波那契數列和計算階乘這兩種,書本上大多也是以這兩個為例,下面就簡單介紹一下這兩個吧。 斐波那契數列: 1 1 2 3 5 8......這樣的數列叫做斐波那契數列,規律很簡單,前兩個數字的和就是第三個

用棧實現二叉樹的遍歷

先實現棧 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef  char ElemType; #define STACKSIZE 20 typedef st

用鄰接表儲存有向圖並實現DFS+非BFS兩種遍歷

程式碼如下: #include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> using namespace std; #define MA

二叉查詢樹的實現插入+呼叫

package BinaryTree; public class BinarySearchTree { public TreeNode root; public BinarySearchTree(){ //root=new TreeNode(1,"A");

利用鄰接矩陣儲存無向圖,並實現BFS DFS+非兩種遍歷

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> using namespace std; //------------鄰接矩陣----------- #def

演算法-一步步教你如何用c語言實現堆排序

看了左神的堆排序,覺得思路很清晰,比常見的遞迴的堆排序要更容易理解,所以自己整理了一下筆記,帶大家一步步實現堆排序演算法 首先介紹什麼是大根堆:每一個子樹的最大值都是子樹的頭結點,即根結點是所有結點的最大值 堆排序是基於陣列和二叉樹思想實現的(二叉樹是腦補結構,實際是陣列) 堆排序過程 1、陣列建

【資料結構】二叉樹的建立和遍歷

該程式使用的是遞迴地建立方法,以及非遞迴的遍歷演算法 執行環境:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild