[java8]如何用函數(shù)式思想來(lái)解決樹(shù)搜索

搜索樹(shù)是一個(gè)常見(jiàn)的操作,可分成深度搜索和廣度搜索。今天,本文將利用函數(shù)式開(kāi)發(fā)思想,不使用遞歸而僅用java8的stream類實(shí)現(xiàn)深度搜索和廣度搜索。(筆者建議,閱讀本文前,需對(duì)java8中stream操作有基礎(chǔ)性的了解。)

java8函數(shù)式開(kāi)發(fā)

開(kāi)始之前,先創(chuàng)建一個(gè)樹(shù)節(jié)點(diǎn)。

public class Node {
    private Node left;
    private Node right;
    private String label;

    public Node(String label, Node left, Node right) {
        this.left = left;
        this.right = right;
        this.label = label;
    }

    public List<Node> getChildren() {
        return Stream.of(left, right)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

}

這是樹(shù)節(jié)點(diǎn)的類。每個(gè)節(jié)點(diǎn)可以有0,1,2個(gè)孩子。如果孩子不存在,則為null。

getChildren() 是用來(lái)返回節(jié)點(diǎn)的孩子集合。大致的實(shí)現(xiàn)原理是,先用左孩子和右孩子創(chuàng)建List,并stream這個(gè)List,然后用filter過(guò)濾掉null,然后收集(collect)剩下的數(shù)據(jù)到一個(gè)新的list并返回。

深度搜索

我們需要一個(gè)方法,返回一個(gè)含有所有node的List,順序是深度搜索的順序:

public class Node {
    //...
    public List<Node> searchByDepth() {
        List<Node> visitedNodes = new LinkedList<>();
        List<Node> unvisitedNodes = Arrays.asList(this);

        while(!unvisitedNodes.isEmpty()) {
            Node currNode = unvisitedNodes.remove(0);

            List<Node> newNodes = currNode.getChildren()
                    .stream()
                    .filter(node -> !visitedNodes.contains(node))
                    .collect(Collectors.toList());

            unvisitedNodes.addAll(0, newNodes);
            visitedNodes.add(currNode);
        }

        return visitedNodes;
    }

}

我們有2個(gè)list,分別存放已訪問(wèn)節(jié)點(diǎn)的list(visitedNodes)和待訪問(wèn)節(jié)點(diǎn)的list(unvisitedNodes)。開(kāi)始搜索前,先添加根節(jié)點(diǎn)到unvisitedNodes。

開(kāi)始循環(huán)訪問(wèn)unvisitedNodes中的節(jié)點(diǎn)。我們先取第一節(jié)點(diǎn),作為當(dāng)前節(jié)點(diǎn),然后把他的孩子節(jié)點(diǎn)進(jìn)行stream,過(guò)濾掉已訪問(wèn)過(guò)的,并收集回List。把這個(gè)List加到unvisitedNodes的開(kāi)頭。這樣做,就是為了深度搜索。最后將當(dāng)前節(jié)點(diǎn)加到visitedNodes中。

反復(fù)循環(huán),直到unvisitedNodes中沒(méi)有節(jié)點(diǎn),即表示搜索完成,返回visitedNodes作為結(jié)果。

廣度搜索

另寫(xiě)一個(gè)方法,返回一個(gè)含有所有node的List,順序是廣度搜索的順序:

public class Node {
   //...
    public List<Node> searchByBreadth() {
        List<Node> visitedNodes = new LinkedList<>();
        List<Node> unvisitedNodes = Arrays.asList(this);

        while(!unvisitedNodes.isEmpty()) {
            List<Node> newNodes = unvisitedNodes
                    .stream()
                    .map(Node::getChildren)
                    .flatMap(List::stream)
                    .filter(node -> !visitedNodes.contains(node))
                    .collect(Collectors.toList());

            visitedNodes.addAll(unvisitedNodes);
            unvisitedNodes = newNodes;
        }

        return visitedNodes;
    }

}

廣度搜索的開(kāi)始和深度搜索一樣,準(zhǔn)備了2個(gè)List。廣度搜索是按樹(shù)的層次來(lái)搜索新節(jié)點(diǎn)。所以的做法是找到當(dāng)前層次所對(duì)應(yīng)的下一個(gè)層次的所有節(jié)點(diǎn),并把這些節(jié)點(diǎn)全部加到unvisitedNodes中。

我們使用map來(lái)得到下一層次的節(jié)點(diǎn):

.map(Node::getChildren)

但是這里得到是類型是List<Node>的stream,故需使用flatMap,將其變成類型是Node的stream。在過(guò)濾掉已經(jīng)訪問(wèn)的節(jié)點(diǎn)之后,收集到的節(jié)點(diǎn)List作為unvistedNodes。而原來(lái)的unvistedNodes中的節(jié)點(diǎn)全部放置到visitedNodes中。

同樣,當(dāng)unvisitedNodes中沒(méi)有節(jié)點(diǎn),即表示搜索完成,返回visitedNodes作為結(jié)果。

總結(jié)

使用了函數(shù)式思想的stream類之后,不論是代碼的可讀性還是簡(jiǎn)潔性上,都比不使用stream類好太多。

非常感謝您的閱讀!您的留言、打賞、點(diǎn)贊、關(guān)注、分享,對(duì)筆者最大的鼓勵(lì):P

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評(píng)論 19 139
  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)。 張土汪:刷leetcod...
    土汪閱讀 12,921評(píng)論 0 33
  • 誠(chéng)實(shí)面對(duì)自己。 愛(ài)人不親,反其仁; 治人不治,反其智; 禮人不答,反其敬。
    陳錦鴻立陽(yáng)企業(yè)閱讀 529評(píng)論 0 0
  • 艾莉小姐住在Z城的郊外,是一個(gè)偏僻的地方。但是艾莉小姐從未在意,她甚至喜歡她住處的環(huán)境。我最喜歡艾莉小姐的露天陽(yáng)臺(tái)...
    奐肖閱讀 317評(píng)論 0 0
  • 我要走一條漫長(zhǎng)的路 有時(shí)候 會(huì)有人在路上等我 他執(zhí)拗地要陪我走上一小段 有時(shí)候 他會(huì)問(wèn)我是否愿意 讓他陪我走完這條...
    信仰_5fd8閱讀 267評(píng)論 0 1

友情鏈接更多精彩內(nèi)容