基於深度優先的層次排序

有一種數據,例如組織結構,在批量添加到系統中時,需要先從根節點開始添加,然後是第二層組織、第三層組織…而批量添加的數據有時並不是有序的,這就需要對數據做一個排序的預處理,這樣的排序我把它稱爲按層次排序。

  1. 背景
    有一些組織的數據,可能是從csv等文本中導入的,也可能是從第三方系統中同步的,且數據量可能會很大,我們需要對這樣的數據做層次排序後批量插入到我方的數據庫中。還有一種特殊情況,就是可能數據已經同步過一次了,到第二次同步時已經不再是一個完整的組織了。
  2. 解決思路
    (1)原始數據中將父節點相同的放在一起
    (2)按照深度優先的原則,從第一個父節點開始構造組織樹,找以它的子節點爲父節點的數據,直到將第一個父節點下所有的子孫節點都找到爲止,已經遍歷過的數據做刪除標記,下次不再處理
    (3)從第二個父節點開始,重複步驟(2),考慮結果是多顆子樹的情況
    (4)使用隊列對結果做樹的層次遍歷,目的就是將結果再轉換回一條一條的組織數據,而不是樹,方便做批量插入。
  3. 實現
    (1)原始數據中將父節點相同的放在一起
/**
     * 將原始數據中pid相同的放在一起
     *
     * @param originalData
     * @return
     */
    public List<OrgOriginalDto> orgDataDeal(List<OrgOriginalDto> originalData){
        List<OrgTreeNode> nodesList = parseNode(originalData);
        if(ObjectUtils.isEmpty(nodesList)){
            return null;
        }
        Map<String, MidDataDto> map = new HashMap<>(16);
        nodesList.forEach(item->{
            if(map.containsKey(item.getPid())){
                map.get(item.getPid()).getData().add(item);
            }else{
                MidDataDto midDataDto = new MidDataDto();

                List<OrgTreeNode> children = new ArrayList<>();
                children.add(item);
                midDataDto.setData(children);
                map.put(item.getPid(), midDataDto);
            }
        });
        buildTree(map);

        List<OrgOriginalDto> result = getLevelList(map);

        return result;
    }

(2)按深度優先構造組織樹

private void buildTree(Map<String, MidDataDto> data){
        if(!ObjectUtils.isEmpty(data)){
            data.forEach((k,v)->{
                if(!v.isDelete()){
                    findChildrenByRecursion(v, data);
                }
            });
        }
    }

    private void findChildrenByRecursion(MidDataDto source, Map<String, MidDataDto> data){
        if(!source.isDelete() && !ObjectUtils.isEmpty(source.getData())){
            source.getData().forEach(item->{
                String nextLevelPid = item.getId();
                if (data.containsKey(nextLevelPid)) {
                    MidDataDto midDataDto = data.get(nextLevelPid);
                    item.setChildren(midDataDto.getData());
                    if(!midDataDto.isDelete()){
                        findChildrenByRecursion(midDataDto, data);
                    }
                    midDataDto.setDelete(true);
                }
            });
        }
    }

(3)層次遍歷法再將樹平鋪成list

/**
     * 將樹再平鋪成list,使用樹的層次遍歷法
     *
     * @param data
     * @return
     */
    private List<OrgOriginalDto> getLevelList(Map<String, MidDataDto> data){
        List<OrgOriginalDto> result = new ArrayList<>();
        Queue<OrgTreeNode> queue = new LinkedList<>();
        data.forEach((k,v)->{
            if(!v.isDelete()){
                v.getData().forEach(item->{
                    result.add(parseOrgDto(item));
                    queue.offer(item);
                });
                treeTraverseByLevel(result, queue);
            }
        });
        return result;
    }

    private void treeTraverseByLevel(List<OrgOriginalDto> list, Queue<OrgTreeNode> queue){
        while(!queue.isEmpty()){
            OrgTreeNode orgTreeNode = queue.poll();
            if(!ObjectUtils.isEmpty(orgTreeNode.getChildren())){
                orgTreeNode.getChildren().forEach(item->{
                    list.add(parseOrgDto(item));
                    queue.offer(item);
                });
            }
        }
    }

最後,模擬數據測試

@Component
public class TestInit implements CommandLineRunner {

    @Autowired
    private OriginalDataHandler originalDataHandler;

    @Override
    public void run(String... strings) throws Exception {
        List<OrgOriginalDto> data = new ArrayList<>();

        OrgOriginalDto o11 = new OrgOriginalDto();
        o11.setId("4");
        o11.setPid("2");
        o11.setOrgName("org11");
        data.add(o11);

        OrgOriginalDto o1 = new OrgOriginalDto();
        o1.setId("2");
        o1.setPid("1");
        o1.setOrgName("org1");
        data.add(o1);

        OrgOriginalDto o2 = new OrgOriginalDto();
        o2.setId("3");
        o2.setPid("1");
        o2.setOrgName("org2");
        data.add(o2);
        
        OrgOriginalDto o0 = new OrgOriginalDto();
        o0.setId("1");
        o0.setPid("-1");
        o0.setOrgName("root");
        data.add(o0);

        //對原始數據進行重新排序
        List<OrgOriginalDto> orgList =  originalDataHandler.orgDataDeal(data);
        soutList(orgList);
        //批量插入
    }

    private void soutList(List<OrgOriginalDto> list){
        if(!ObjectUtils.isEmpty(list)){
            list.forEach(item->{
                System.out.println(item);
            });
        }
    }
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章