二叉樹的遍歷 遞歸 非遞歸 Java

二叉樹的常用遍歷算法實現(xiàn)

二叉樹的形狀.png

首先定義 二叉樹千埃,節(jié)點值憔儿,左子節(jié)點,右子節(jié)點

public class TreeNode {
    private int value;
    private TreeNode left;
    private TreeNode right;
    public TreeNode(int value) {
        this.value = value;
    }
}

前序遍歷

  • 遞歸實現(xiàn)
public static void preOrder(TreeNode treeNode) {
    if (treeNode != null) {
        System.out.print(treeNode.value + " ");
        preOrder(treeNode.left);
        preOrder(treeNode.right);
    }
}
  • 非遞歸實現(xiàn)(1)
    • 這個是常規(guī)思路放可,先遍歷到根節(jié)點谒臼,并打印、壓棧耀里,然后遍歷其左子節(jié)點蜈缤,打印、壓棧冯挎。
    • 若左子節(jié)點已經(jīng)是葉子節(jié)點底哥,則 while 循環(huán)結(jié)束,開始從棧中彈出根節(jié)點,并訪問根節(jié)點的右子樹趾徽,直到棧為空续滋,則遍歷結(jié)束。
public static void preOrderStack(TreeNode treeNode) {

    if (treeNode == null) {
        return;
    }
    Stack<TreeNode> stack = new Stack<TreeNode>();
    TreeNode root = treeNode;
    
    while (root != null || !stack.isEmpty()) {
        while (root != null) {
            //  注意和 中序非遞歸的區(qū)別孵奶,這里是先打印節(jié)點值疲酌,再去加入棧中
            System.out.print(root.value + " ");
            stack.push(root);
            root = root.left;
        }
        if (!stack.isEmpty()) {
            root = stack.pop();
            root = root.right;
        }
    }
}
  • 非遞歸實現(xiàn)(2)
    • 這里的思路是利用棧先進(jìn)后出的性質(zhì), 先將根節(jié)點入棧
    • 循環(huán)中了袁,彈出根節(jié)點朗恳,然后先加入右子節(jié)點,后加入左子節(jié)點
    • 再一次循環(huán)载绿,新彈出的節(jié)點就是剛壓入的左子節(jié)點
public static void preOrderStack1(TreeNode treeNode) {
    if (treeNode == null) {
        return;
    }
    Stack<TreeNode> stack = new Stack<TreeNode>();
    stack.push(treeNode);
    while (!stack.isEmpty()) {
        TreeNode root = stack.pop();
        System.out.print(root.value + " ");
        if (root.right != null) {
            stack.push(root.right);
        }
        if (root.left != null) {
            stack.push(root.left);
        }
    }
}

中序遍歷

  • 遞歸實現(xiàn)
public static void midOrder(TreeNode treeNode) {
    if (treeNode != null) {
        midOrder(treeNode.left);
        System.out.print(treeNode.value + " ");
        midOrder(treeNode.right);
    }
}
  • 非遞歸實現(xiàn)
    • 中序遍歷是 左 根 右
public static void midOrderStack(TreeNode treeNode) {
    if (treeNode == null) {
        return;
    }
    Stack<TreeNode> stack = new Stack<TreeNode>();
    while (treeNode != null || !stack.isEmpty()) {
        while (treeNode != null) {
            stack.push(treeNode);
            treeNode = treeNode.left;
        }
        if (!stack.isEmpty()) {
            treeNode = stack.pop();
            System.out.print(treeNode.value + " ");
            treeNode = treeNode.right;
        }
    }
}

后序遍歷

  • 遞歸實現(xiàn)
public static void postOrder(TreeNode treeNode) {
    if (treeNode != null) {
        postOrder(treeNode.left);
        postOrder(treeNode.right);
        System.out.print(treeNode.value + " ");
    }
}
  • 非遞歸實現(xiàn)
    • 由于后序遍歷二叉樹的順序是 左 右 根粥诫,故可以使用中間臨時棧保存 根 右 左 的遍歷結(jié)果
public static void postOrderStack(TreeNode treeNode) {
    Stack<TreeNode> stack = new Stack<TreeNode>();
    Stack<TreeNode> stack1 = new Stack<TreeNode>();
    while (treeNode != null || !stack.isEmpty()) {
        while (treeNode != null) {
            stack.push(treeNode);
            stack1.push(treeNode);
            treeNode = treeNode.right;
        }
        if (!stack.isEmpty()) {
            treeNode = stack.pop();
            treeNode = treeNode.left;
        }
    }
    while (!stack1.isEmpty()) {
        TreeNode treeNode1 = stack1.pop();
        System.out.print(treeNode1.value + " ");
    }
}

層次遍歷

public static void levelOrder(TreeNode treeNode) {
    if (treeNode == null) {
        return;
    }
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(treeNode);
    while (!queue.isEmpty()) {
        TreeNode root = queue.poll();
        System.out.print(root.value + " ");

        if (root.left != null) {
            queue.add(root.left);
        }
        if (root.right != null) {
            queue.add(root.right);
        }
    }
}

“之” 字型遍歷

public static void printZ(TreeNode treeNode){
    if (treeNode == null){
        return;
    }
    Stack<TreeNode> stack1 = new Stack<>();
    Stack<TreeNode> stack2 = new Stack<>();
    stack1.push(treeNode);
    boolean flag = true;
    while (!stack1.isEmpty() || !stack2.isEmpty()){
        if (flag){
            while (!stack1.isEmpty()) {
                treeNode = stack1.pop();
                System.out.print(treeNode.value+" ");
                if (treeNode.left != null) {
                    stack2.add(treeNode.left);
                }
                if (treeNode.right != null) {
                    stack2.add(treeNode.right);
                }
            }
        }else {
            while (!stack2.isEmpty()) {
                treeNode = stack2.pop();
                System.out.print(treeNode.value+" ");
                if (treeNode.right != null) {
                    stack1.add(treeNode.right);
                }
                if (treeNode.left != null) {
                    stack1.add(treeNode.left);
                }
            }
        }
        flag = flag ^ true;
    }
}

求最大深度

  • 遞歸實現(xiàn)
public static int getDepth(TreeNode treeNode) {
    if (treeNode == null) {
        return 0;
    }
    int left = getDepth(treeNode.left);
    int right = getDepth(treeNode.right);
    return left > right ? left + 1 : right + 1;
}
  • 非遞歸實現(xiàn)
public static void getDepth1(TreeNode treeNode){
    if (treeNode == null){
        return;
    }
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(treeNode);
    int depth = 0;
    while (!queue.isEmpty()){
        int size = queue.size();
        while (size > 0){
            treeNode = queue.poll();
            if (treeNode.left != null){
                queue.add(treeNode.left);
            }
            if (treeNode.right != null){
                queue.add(treeNode.right);
            }
            size--;
        }
        depth++;
    }
    System.out.println(depth);
}

求最大寬度

  • 對于二叉樹,每一層的節(jié)點數(shù)是不一樣的崭庸,計算二叉樹中具有結(jié)點數(shù)最多的那一層的結(jié)點個數(shù)
    用隊列實現(xiàn)怀浆,實質(zhì)就是當(dāng)當(dāng)前層節(jié)點彈出,加入下一層節(jié)點
public static void getWidth(TreeNode treeNode) {
    // 根節(jié)點為空冀自,則 最大寬度為 0 揉稚;
    if (treeNode == null) {
        return;
    }
    // 此時根節(jié)點入隊,最大寬度為 1 
    int max = 1;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(treeNode);

    // 注意循環(huán)條件:隊列不為空
    while (!queue.isEmpty()) {
        // 記錄當(dāng)前層的節(jié)點數(shù)
        int size = queue.size();  

        // 把當(dāng)前層的所有節(jié)點依次彈出熬粗,循環(huán)結(jié)束時,當(dāng)前層節(jié)點會全部彈出余境,此時隊列中只包含下一層的所有節(jié)點
        while (size > 0) {   

            treeNode = queue.poll();
            size--;

            // 彈出的同時把下一層的節(jié)點加入隊列中
            if (treeNode.left != null) {
                queue.add(treeNode.left);   
            }
            if (treeNode.right != null) {
                queue.add(treeNode.right);
            }
        }
       // 此時隊列中存放的是下一層的所有節(jié)點驻呐,比較它與上一層節(jié)點的個數(shù),保存較大值
        if (max < queue.size()) {
            max = queue.size();
        }
    }
    System.out.println(max);
}

數(shù)組列表實現(xiàn)求最大寬度芳来,類比隊列含末,用兩個指針 i,j即舌。i 指向當(dāng)前層節(jié)點第一個佣盒,j 指向當(dāng)前層最后一個。彈出時 i++ 顽聂,加入元素時 j++肥惭;

public static void levelOrder(TreeNode treeNode){
    if (treeNode == null){
        return;
    }
    List<TreeNode> list = new ArrayList<TreeNode>();
    list.add(treeNode);
    int max = 1;
    int i =0;
    int j = 0;
    // i,j 最多相等紊搪,遍歷到最后一個元素時蜜葱,i = j ,此時 i 節(jié)點左右子節(jié)點為空耀石,下一步 i 將 大于 j 牵囤,循環(huán)結(jié)束
    while (i <= j){
        // 臨時保存當(dāng)前層的節(jié)點數(shù)
        int size = j-i+1;
        while (size > 0){
            treeNode = list.get(i);
            if (treeNode.left != null){
                list.add(treeNode.left);
                j++;
            }
            if (treeNode.right != null){
                list.add(treeNode.right);
                j++;
            }
            size--;
            i++;
        }
        if (max < j-i+1){
            max = j-i+1;
        }
    }
    System.out.println(max);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子揭鳞,更是在濱河造成了極大的恐慌炕贵,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件野崇,死亡現(xiàn)場離奇詭異鲁驶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)舞骆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門钥弯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人督禽,你說我怎么就攤上這事脆霎。” “怎么了狈惫?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵睛蛛,是天一觀的道長。 經(jīng)常有香客問我胧谈,道長忆肾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任菱肖,我火速辦了婚禮客冈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘稳强。我一直安慰自己场仲,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布退疫。 她就那樣靜靜地躺著渠缕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褒繁。 梳的紋絲不亂的頭發(fā)上亦鳞,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音棒坏,去河邊找鬼燕差。 笑死,一個胖子當(dāng)著我的面吹牛俊抵,可吹牛的內(nèi)容都是我干的谁不。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼徽诲,長吁一口氣:“原來是場噩夢啊……” “哼刹帕!你這毒婦竟也來了吵血?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤偷溺,失蹤者是張志新(化名)和其女友劉穎蹋辅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挫掏,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡侦另,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尉共。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褒傅。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖袄友,靈堂內(nèi)的尸體忽然破棺而出殿托,到底是詐尸還是另有隱情,我是刑警寧澤剧蚣,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布支竹,位于F島的核電站,受9級特大地震影響鸠按,放射性物質(zhì)發(fā)生泄漏礼搁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一目尖、第九天 我趴在偏房一處隱蔽的房頂上張望馒吴。 院中可真熱鬧,春花似錦卑雁、人聲如沸募书。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鬼吵,卻和暖如春扣甲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背齿椅。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工琉挖, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涣脚。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓示辈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親遣蚀。 傳聞我的和親對象是個殘疾皇子矾麻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容