1. 程式人生 > >用遞迴演算法得到Java的樹形結構

用遞迴演算法得到Java的樹形結構

要求:得到無限分類的選單欄。並且告訴你任何一個節點,都能得到整個選單。

這裡寫圖片描述

資料庫是mongodb。沒有貼全部,只貼部分。
首先一個整體思路是:
1、要有一個能通過父類id得到全部子類id的集合。
2、遍歷這些子類集合來把它們的關係關聯起來。

程式碼部分:
建立資料庫的查詢:

DBCollection collection = GGMongoOperator.getGGBusinessCollection("gg_help_center_v3");//幫助中心表
    DBObject query = new BasicDBObject("status",1);
    DBObject object
= new BasicDBObject("_id",1).append("name", 1).append("pId", 1).append("icon", 1);
DBCursor cursor = collection.find(query, object).sort(new BasicDBObject("add_time",1)); List<Map<String,Object>> nodeList = GGDBCursor.find(cursor);

其中List<Map<String,Object>> nodeList = GGDBCursor.find(cursor);


是我公司自己封裝的方法。不使用的話,就是用while迴圈自己來組裝資料。
得到的資料格式 key value;比如:
map.put(“_id”,1);
map.put(“name”,”每日頭條”)
list.add(map);

之後就是為思路1做準備。緊接著上面的程式碼:

Map<String,Tree> treeMap = new HashMap<String, Tree>();
Map<String, List<String>> pidMap = new HashMap<String, List<String
>>(); for (Map<String, Object> map : nodeList) { //1、我要拿到頂層節點 Object pId = map.get("pId"); String pid = null; if(pId != null){ pid = pId.toString(); }else{ continue; } //2、通過父類id能拿到全部子類的id if(pidMap.get(pid) == null){ List<String> list = new ArrayList<String>(); list.add(map.get("_id").toString()); pidMap.put(pId.toString(), list); }else{ List<String> list = pidMap.get(pId.toString()); list.add(map.get("_id").toString()); } Tree t = new Tree(); t.setParentId(map.get("pId").toString()); t.setName(map.get("name").toString()); t.setId(map.get("_id").toString()); treeMap.put(map.get("_id").toString(), t); } List<String> fatherId = new ArrayList<String>(pidMap.keySet()); fatherId.remove("0"); //0代表的是頂級目錄的pid,我後面遍歷fatherId獲得相應的實體類,但是頂級pid是沒有相應的實體類的,所以要移除,否則會報null int size = fatherId.size(); int ii = 0; //我用來做測試,迴圈多少次 return getTree(fatherId, nodeList.get(0).get("_id").toString(), treeMap, pidMap, new ArrayList<Tree>(), size, ii);

上面的程式碼中pidMap就是思路1中所說的集合,通過父類id,得到全部的子類id。
treeMap:儲存的就是所有資料的實體類。格式是:key:_id, value:實體類。
Tree:這個是我自己定義的pojo類。
屬性有:id,name,parentId,List<Tree> childrenList = new ArrayList<Tree>();

getTree(…)這個方法就是思路2中去遍歷各個子類集合中把它們的關係關聯起來。
程式碼如下:

private static List<Tree> getTree(List<String> fatherId, String id, Map<String,Tree> treeMap, Map<String, List<String>> pidMap, List<Tree> result,int size,int ii) {
        //先去獲取當前物件,在把子節點的treeset到children裡面去。
        Tree tree = treeMap.get(id); //這個tree就是實體類!
        if(tree !=null){ //正常情況下不會為空,出現為空的情況都是因為有髒資料造成的
            if(tree.getParentId() == null||"0".equals(tree.getParentId())){
                //我這裡應該儲存的是一級物件,子物件都在一級物件裡面
                if(!result.contains(tree)){
                    result.add(tree);
                }
            }

            //若是葉子節點就沒有必要走下面的遍歷
            List<String> childList = pidMap.get(id);
            if(childList != null){
                //以下遍歷是為了找出子節點
                for(String c : childList){
                    Tree childTree = treeMap.get(c);
                    String pid = childTree.getParentId();
                    if(id.equals(pid)){
                        List<Tree> childrenList = tree.getChildrenList();
                        if(!childrenList.contains(childTree)){
                            childrenList.add(childTree);
                        }
                    }
                    ii++;
                }
            }
        }

        if(size <= 0){//結束
            return result;
        }
int ss = size-1;    
result = getTree(fatherId, fatherId.get(ss), treeMap, pidMap, result, ss,ii);
System.out.println("迴圈了======================" + ii);
return result;

    }

詳解————這裡我使用了遞迴:

if(tree.getParentId() == null||"0".equals(tree.getParentId())){
        //我這裡應該儲存的是一級物件,子物件都在一級物件childrenList裡面
        if(!result.contains(tree)){
            result.add(tree);
        }
}

這段程式碼的意思就是當它是頂級節點時就加入result。我這麼寫是假設它有多個頂級節點。一般情況下就一個。↑

        //若是葉子節點就沒有必要走下面的遍歷
        List<String> childList = pidMap.get(id);
        if(childList != null){
            //以下遍歷是為了找出子節點
            for(String c : childList){
                Tree childTree = treeMap.get(c);
                String pid = childTree.getParentId();
                if(id.equals(pid)){
                    List<Tree> childrenList = tree.getChildrenList();
                    if(!childrenList.contains(childTree)){
                        childrenList.add(childTree);
                    }
                }
                ii++;
            }
        }

這個遍歷就是去遍歷子類節點集合把它們都新增到父類的childrenList中去。↑

        if(size <= 0){//結束
            return result;
        }
        int ss = size-1;

    result = getTree(fatherId, fatherId.get(ss), treeMap, pidMap, result, ss,ii);
        System.out.println("迴圈了======================" + ii);
        return result;

這塊程式碼是進行遞迴的關鍵。每次遞迴時,我都從fatherId中取一個id。
fatherId儲存的是所有具有子類的id集合,也就是說,葉子節點不在裡面,在裡面的都是有子類的;↑

還有個要注意的地方:

if(!childrenList.contains(childTree)){
    childrenList.add(childTree);
}

這裡使用contains是因為childTree是個list陣列,不會去重。若不去重,就會造成重複資料。↑

自此關鍵程式碼就結束啦!

public class Tree {

    /**
     * 節點唯一標識
     */
    private String id;

    /**
     * 節點名稱
     */
    private String name;

    /**
     * 所屬父節點ID
     */
    private String parentId;

    /**
     * 所屬父節點物件
     */
    //private Tree parentObj;

    /**
     * 所含子節點
     */
    private List<Tree> childrenList = new ArrayList<Tree>();

就不詳細列出來。。。

完整的程式碼:

    public static List<Tree> getAllNodes2(){
        DBCollection collection = GGMongoOperator.getGGBusinessCollection("gg_help_center_v3");//幫助中心表
        DBObject query = new BasicDBObject("status",1);
        DBObject object = new BasicDBObject("_id",1).append("name", 1).append("pId", 1).append("icon", 1);
        DBCursor cursor = collection.find(query, object).sort(new BasicDBObject("add_time",1));
        List<Map<String,Object>> nodeList = GGDBCursor.find(cursor);

        Map<String,Tree> treeMap = new HashMap<String, Tree>();
        Map<String, List<String>> pidMap = new HashMap<String, List<String>>();

        for (Map<String, Object> map : nodeList) {
            //1、我要拿到頂層節點
            Object pId = map.get("pId");
            String pid = null;
            if(pId != null){
                pid = pId.toString();
            }else{
                continue;
            }
            //2、通過父類id能拿到全部子類的id
            if(pidMap.get(pid) == null){
                List<String> list = new ArrayList<String>();
                list.add(map.get("_id").toString());
                pidMap.put(pId.toString(), list);
            }else{
                List<String> list = pidMap.get(pId.toString());
                list.add(map.get("_id").toString());
            }

            Tree t = new Tree();
            t.setParentId(map.get("pId").toString());
            t.setName(map.get("name").toString());
            t.setId(map.get("_id").toString());
            treeMap.put(map.get("_id").toString(), t);
        }
        List<String> fatherId = new ArrayList<String>(pidMap.keySet());
        fatherId.remove("0");
        int size = fatherId.size();
        int ii = 0;
        return getTree(fatherId, nodeList.get(0).get("_id").toString(), treeMap, pidMap, new ArrayList<Tree>(), size, ii);

    }
    private static List<Tree> getTree(List<String> fatherId, String id, Map<String,Tree> treeMap, Map<String, List<String>> pidMap, List<Tree> result,int size,int ii) {
        //先去獲取當前物件,在把子節點的treeset到children裡面去。

        Tree tree = treeMap.get(id); //這個tree就是那個固定的常量!
        if(tree !=null){
            if(tree.getParentId() == null||"0".equals(tree.getParentId())){
                //我這裡應該儲存的是一級物件,子物件都在一級物件裡面
                if(!result.contains(tree)){
                    result.add(tree);
                }
            }

            //若是葉子節點就沒有必要走下面的遍歷
            List<String> childList = pidMap.get(id);
            if(childList != null){
                //以下遍歷是為了找出子節點
                for(String c : childList){
                    Tree childTree = treeMap.get(c);
                    String pid = childTree.getParentId();
                    if(id.equals(pid)){
                        List<Tree> childrenList = tree.getChildrenList();
                        if(!childrenList.contains(childTree)){
                            childrenList.add(childTree);
                        }
                    }
                    ii++;
                }
            }
        }
        if(size <= 0){//結束
            return result;
        }
        int ss = size-1;

        result = getTree(fatherId, fatherId.get(ss), treeMap, pidMap, result, ss,ii);
        System.out.println("迴圈了======================" + ii);
        return result;

    }

再加上Tree這個pojo類,就OK啦!