演算法知識梳理(11) - 二叉樹相關演算法第一部分

澤毛發表於2017-12-21

二叉樹演算法系列文章

演算法知識梳理(11) - 二叉樹相關演算法第一部分
演算法知識梳理(12) - 二叉樹相關演算法第二部分
演算法知識梳理(13) - 二叉樹相關演算法第三部分

一、概述

演算法知識梳理(10) - 二叉查詢樹 中,我們簡要介紹了二叉查詢樹的基本概念、插入及刪除操作,今天這篇文章,以這個為基礎,介紹一下和二叉樹有關的操作,由於篇幅有限,因此分為三篇文章介紹,第一部分包括:

  • 遞迴遍歷二叉樹(先序遍歷、中序遍歷、後序遍歷)
  • 分層列印二叉樹
  • 列印二叉樹的第n
  • 統計二叉樹葉結點的個數
  • 統計二叉樹的高度

二、程式碼實現

2.1 遞迴遍歷

解決思路

二叉樹的遍歷方式有以下三種:

  • 先序遍歷:根結點 -> 左子樹 -> 右子樹
  • 中序遍歷:左子樹 -> 根結點 -> 右子樹
  • 後序遍歷:左子樹 -> 右子樹 -> 根結點

遞迴的方式比較容易理解,只需要改變函式呼叫和列印元素值語句之間的順序即可,例如先序遍歷,就先列印該結點元素的值,再分別列印左子樹和右子樹即可。

程式碼實現

class Untitled {

    static class Tree {
        int size;
        Node root;
    }

    static class Node {
        Node parent;
        Node left;
        Node right;
        int value;
    }

    static void insertNode(Tree tree, int value) {
        if (tree == null) {
            return;
        }
        Node tNode = tree.root;
        //待插入結點的父結點,如果遍歷完為空,說明此時是一個空樹。
        Node pNode = null;
        //新的結點。
        Node nNode = new Node();
        nNode.value = value;
        while (tNode != null) {
            pNode = tNode;
            if (tNode.value > value) {
                tNode = tNode.left;
            } else {
                tNode = tNode.right;
            }
        }
        nNode.parent = pNode;
        if (pNode == null) {
            tree.root = nNode;
        } else if (pNode.value > value) {
            pNode.left = nNode;
        } else {
            pNode.right = nNode;
        }
        tree.size++;
    }

    static Tree createBinTree(int p[], int len) {
        Tree tree = new Tree();
        for (int i = 0; i < len; i++) {
            int value = p[i];
            insertNode(tree, value);
        }
        return tree;
    }

    static void printPreOrder(Node node) {
        if (node == null) {
            return;
        }
        //先列印根結點。
        System.out.println(node.value);
        //先遍歷左子樹。
        printPreOrder(node.left);
        //再遍歷右子樹。
        printPreOrder(node.right);
    }


    static void printInOrder(Node node) {
        if (node == null) {
            return;
        }
        printInOrder(node.left);
        //遍歷完左子樹後,列印根結點,最後遍歷右子樹。
        System.out.println(node.value);
        printInOrder(node.right);
    }

    static void printPostOrder(Node node) {
        if (node == null) {
            return;
        }
        //先遍歷右子樹。
        printPostOrder(node.right);
        //再遍歷左子樹。
        printPostOrder(node.left);
        //最後列印根結點。
        System.out.println(node.value);
    }

    public static void main(String[] args) {
        int p[] = {3, 5, 6, 1, 2, 4};
        Tree tree = createBinTree(p, p.length);
        System.out.println("- 先序遍歷 - ");
        printPreOrder(tree.root);
        System.out.println("- 中序遍歷 - ");
        printInOrder(tree.root);
        System.out.println("- 後序遍歷 - ");
        printPostOrder(tree.root);
    }
}
複製程式碼

執行結果

- 先序遍歷 - 
3
1
2
5
4
6
- 中序遍歷 - 
1
2
3
4
5
6
- 後序遍歷 - 
6
4
5
2
1
3
複製程式碼

2.2 分層列印二叉樹

解決思路

假設建立了一棵如下的二叉樹,元素3為第一層,1、5為第二層,-1、2、4、6為第三層,要求按照層的順序依次列印出每一層的所有元素。

二叉樹
這裡需要藉助容器類LinkedList來進行儲存下一行的元素,並用end儲存每一行的最後一個元素,遍歷到該元素時列印換行符來實現分層列印。

程式碼實現

import java.util.LinkedList;

public class Untitled {

    static class Tree {
        int size;
        Node root;
    }

    static class Node {
        Node parent;
        Node left;
        Node right;
        int value;
    }

    static void insertNode(Tree tree, int value) {
        if (tree == null) {
            return;
        }
        Node tNode = tree.root;
        //待插入結點的父結點,如果遍歷完為空,說明此時是一個空樹。
        Node pNode = null;
        //新的結點。
        Node nNode = new Node();
        nNode.value = value;
        while (tNode != null) {
            pNode = tNode;
            if (tNode.value > value) {
                tNode = tNode.left;
            } else {
                tNode = tNode.right;
            }
        }
        nNode.parent = pNode;
        if (pNode == null) {
            tree.root = nNode;
        } else if (pNode.value > value) {
            pNode.left = nNode;
        } else {
            pNode.right = nNode;
        }
        tree.size++;
    }

    static Tree createBinTree(int p[], int len) {
        Tree tree = new Tree();
        for (int i = 0; i < len; i++) {
            int value = p[i];
            insertNode(tree, value);
        }
        return tree;
    }

    static void printTreeLevelOrder(Tree tree) {
        if (tree == null || tree.root == null) {
            return;
        }
        Node root = tree.root;
        LinkedList<Node> queue = new LinkedList<>();
        //先放入根結點。
        queue.offer(root);
        //對應於下一層的最後一個元素。
        Node end = root;
        while (!queue.isEmpty()) {
            //取出當前佇列中的首結點,並列印它的值。
            Node node = queue.getFirst();
            System.out.print(String.valueOf(node.value));
            //將該結點的左右孩子放入到佇列的尾部。
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
            //如果當前的結點是該行的尾結點,那麼列印一個換行符,並且將 end 賦值為下一行的尾結點。
            if (node == end) {
                System.out.print("\n");
                end = queue.getLast();
            } else {
                System.out.print(" ");
            }
            //將該結點從佇列頭部刪除。
            queue.pop();
        }
    }


    public static void main(String[] args) {
        int p[] = {3, 5, 6, 1, 2, 4, -1, -3};
        Tree tree = createBinTree(p, p.length);
        printTreeLevelOrder(tree);
    }
}
複製程式碼

執行結果

>> 3
>> 1 5
>> -1 2 4 6
>> -3
複製程式碼

2.3 列印二叉樹的第 n 層

解決思路

列印二叉樹的第n層和2.2中是相同的原理,只不過是在遍歷到第n層時才列印所需的元素。

程式碼實現

import java.util.LinkedList;

public class Untitled {

    static class Tree {
        int size;
        Node root;
    }

    static class Node {
        Node parent;
        Node left;
        Node right;
        int value;
    }

    static void insertNode(Tree tree, int value) {
        if (tree == null) {
            return;
        }
        Node tNode = tree.root;
        //待插入結點的父結點,如果遍歷完為空,說明此時是一個空樹。
        Node pNode = null;
        //新的結點。
        Node nNode = new Node();
        nNode.value = value;
        while (tNode != null) {
            pNode = tNode;
            if (tNode.value > value) {
                tNode = tNode.left;
            } else {
                tNode = tNode.right;
            }
        }
        nNode.parent = pNode;
        if (pNode == null) {
            tree.root = nNode;
        } else if (pNode.value > value) {
            pNode.left = nNode;
        } else {
            pNode.right = nNode;
        }
        tree.size++;
    }

    static Tree createBinTree(int p[], int len) {
        Tree tree = new Tree();
        for (int i = 0; i < len; i++) {
            int value = p[i];
            insertNode(tree, value);
        }
        return tree;
    }

    static void printTreeKLevel(Tree tree, int k) {
        if (tree == null || tree.root == null) {
            return;
        }
        Node root = tree.root;
        LinkedList<Node> queue = new LinkedList<>();
        //先放入根結點。
        queue.offer(root);
        //對應於下一層的最後一個元素。
        Node end = root;
        int curLevel = 1;
        while (!queue.isEmpty()) {
            //取出當前佇列中的首結點,並列印它的值。
            Node node = queue.getFirst();
            //如果當前位於第k層,那麼就列印元素。
            if (curLevel == k) {
                System.out.print(String.valueOf(node.value));
            }
            //將該結點的左右孩子放入到佇列的尾部。
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
            if (node == end) {
                curLevel++;
                //如果大於k層,那麼就跳出迴圈。
                if (curLevel > k) {
                    break;
                }
                end = queue.getLast();
            } else {
                System.out.print(" ");
            }
            //將該結點從佇列頭部刪除。
            queue.pop();
        }
    }


    public static void main(String[] args) {
        int p[] = {3, 5, 6, 1, 2, 4, -1, -3};
        Tree tree = createBinTree(p, p.length);
        printTreeKLevel(tree, 3);
    }
}
複製程式碼

執行結果

>> -1 2 4 6
複製程式碼

2.4 統計二叉樹的葉結點個數

解決思路

葉結點指的是沒有左右子樹的結點,因此二叉樹的葉結點個數等於它的左右子樹葉結點之和,可以通過遞迴的方法來實現。

程式碼實現

public class Untitled {

    static class Tree {
        int size;
        Node root;
    }

    static class Node {
        Node parent;
        Node left;
        Node right;
        int value;
    }

    static void insertNode(Tree tree, int value) {
        if (tree == null) {
            return;
        }
        Node tNode = tree.root;
        //待插入結點的父結點,如果遍歷完為空,說明此時是一個空樹。
        Node pNode = null;
        //新的結點。
        Node nNode = new Node();
        nNode.value = value;
        while (tNode != null) {
            pNode = tNode;
            if (tNode.value > value) {
                tNode = tNode.left;
            } else {
                tNode = tNode.right;
            }
        }
        nNode.parent = pNode;
        if (pNode == null) {
            tree.root = nNode;
        } else if (pNode.value > value) {
            pNode.left = nNode;
        } else {
            pNode.right = nNode;
        }
        tree.size++;
    }

    static Tree createBinTree(int p[], int len) {
        Tree tree = new Tree();
        for (int i = 0; i < len; i++) {
            int value = p[i];
            insertNode(tree, value);
        }
        return tree;
    }

    static int getLeafNumber(Node node) {
        if (node == null) {
            return 0;
        }
        if (node.left == null && node.right == null) {
            return 1;
        }
        return getLeafNumber(node.left) + getLeafNumber(node.right);
    }

    public static void main(String[] args) {
        int p[] = {3, 5, 6, 1, 2, 4, -1, -3};
        Tree tree = createBinTree(p, p.length);
        System.out.println("葉結點個數=" + getLeafNumber(tree.root));
    }
}
複製程式碼

執行結果

>> 葉結點個數=4
複製程式碼

2.5 統計二叉樹的高度

解決思路

二叉樹的高度 為其根結點到葉結點的最大距離,我們可以先求出它的左右子樹的高度的最大值,再加上1就可得到以該結點為根結點的二叉樹的高度,同樣可以採用遞迴的方式來實現。

程式碼實現

public class Untitled {

    static class Tree {
        int size;
        Node root;
    }

    static class Node {
        Node parent;
        Node left;
        Node right;
        int value;
    }

    static void insertNode(Tree tree, int value) {
        if (tree == null) {
            return;
        }
        Node tNode = tree.root;
        //待插入結點的父結點,如果遍歷完為空,說明此時是一個空樹。
        Node pNode = null;
        //新的結點。
        Node nNode = new Node();
        nNode.value = value;
        while (tNode != null) {
            pNode = tNode;
            if (tNode.value > value) {
                tNode = tNode.left;
            } else {
                tNode = tNode.right;
            }
        }
        nNode.parent = pNode;
        if (pNode == null) {
            tree.root = nNode;
        } else if (pNode.value > value) {
            pNode.left = nNode;
        } else {
            pNode.right = nNode;
        }
        tree.size++;
    }

    static Tree createBinTree(int p[], int len) {
        Tree tree = new Tree();
        for (int i = 0; i < len; i++) {
            int value = p[i];
            insertNode(tree, value);
        }
        return tree;
    }

    static int getTreeHeight(Node node) {
        if (node == null) {
            return 0;
        }
        int left = getTreeHeight(node.left) + 1;
        int right = getTreeHeight(node.right) + 1;
        return left > right ? left : right;
    }

    public static void main(String[] args) {
        int p[] = {3, 5, 6, 1, 2, 4, -1, -3};
        Tree tree = createBinTree(p, p.length);
        System.out.println("二叉樹高度=" + getTreeHeight(tree.root));
    }
}
複製程式碼

執行結果

>> 二叉樹高度=4
複製程式碼

相關文章