遍歷二叉樹的三種方法


朋友面試遇到一道筆試題:寫出遞歸遍歷二叉樹的代碼(先序、中序、后序遍歷都可以)?

首先要知道二叉樹是什么,它的數據結構是怎樣的?

如何實現這種二叉樹?采用匿名內部類的形式實現

class Node{
        //節點數據
        private T data;     //可比較的泛型
        //左子樹
        private Node leftChildTree;
        //右子樹
        private Node rightChildTree;

        public Node(T data){
            this.data = data;
        }
    }

 知道它的數據類型就容易對其進行遍歷

//前序遍歷
    public void preorderTraversal(Node root){
        if (root == null){
            return;
        }
        System.out.println("node = [" + root.data + "]");
        preorderTraversal(root.leftChildTree);
        preorderTraversal(root.rightChildTree);
    }
    //中序遍歷
    public void inorderTraversal(Node root){
        if (root == null){
            return;
        }
        inorderTraversal(root.leftChildTree);
        System.out.println("node = [" + root.data + "]");
        inorderTraversal(root.rightChildTree);
    }

    //后續遍歷
    public void postorderTraversal(Node root){
        if (root == null){
            return;
        }
        postorderTraversal(root.leftChildTree);
        postorderTraversal(root.rightChildTree);
        System.out.println("node = [" + root.data + "]");
    }

但真的這么簡單么?

數據結構和算法問題,解決問題只是看儲備知識的廣度,優化問題才是看能力素質的高度

棧模擬遞歸的非遞歸算法:這樣算法空間復雜度為O(h),h為二叉樹的高度

//前序遍歷
    public void preorderTraversal(Node root){
        if (root == null){
            return;
        }
        Stack<Node> stack = new Stack<Node>();
        stack.push(root);
        while (!stack.empty()){

            Node node = stack.pop();
            System.out.println("node = [" + node.data + "]");
            if (root.rightChildTree != null) stack.push(root.rightChildTree);
            if (root.leftChildTree != null) stack.push(root.leftChildTree);
        }

    }
    //中序遍歷
    public void inorderTraversal(Node root){
        if (root == null){
            return;
        }
        Stack<Node> stack = new Stack<Node>();
        Stack<Node> stack2 = new Stack<Node>();

        stack.push(root);
        while (!stack.empty()){

            if (root.rightChildTree != null) stack.push(root.rightChildTree);
            Node node = stack.pop();
            stack2.push(node);
            if (root.leftChildTree != null) stack.push(root.leftChildTree);
        }
        while(!stack2.empty())
        {
            Node node2 = stack2.pop();
            System.out.println("node = [" + node2.data + "]");
        }
    }

    //后續遍歷
    public void postorderTraversal(Node root){
        if (root == null) return;

        Stack<Node> stack = new Stack<Node>();
        Stack<Node> stack2 = new Stack<Node>();
        stack.push(root);
        while (!stack.empty())
        {
            Node node = stack.pop();
            stack2.push(node);
            if (root.rightChildTree != null) stack.push(root.rightChildTree);
            if (root.leftChildTree != null) stack.push(root.leftChildTree);
        }
        while(!stack2.empty())
        {
            Node node2 = stack2.pop();
            System.out.println("node = [" + node2.data + "]");
        }
    }
View Code

Morris遍歷的神奇之處在於它是非遞歸的算法,但並不需要額外的O(h)的空間,而且復雜度仍然是線性的。

Morrs遍歷優勢:O(1)空間復雜度;二叉樹的形狀不被破壞

1、先找到根節點的左子樹的最右節點,讓最右節點的右子樹指向根節點。(本來最右節點的右子樹是null,將根節點復制給它)

4、以左子樹的根節點的為根節點,繼續1步驟。

3、然后中序遍歷:當左子樹為空時,打印輸出,

         當節點指向的右子樹等於根節點時,說明此節點為根節點左子樹的最右節點,打印根節點,遍歷右子樹。

 //中序遍歷
    public void inorderTraversal(Node root){
        if (root == null){
            return;
        }
        Node node = root;
        while (node != null){
            if (node.leftChildTree == null){
                System.out.println("node = [" + node.data + "]");
                node = node.rightChildTree;

            }else {
                Node temp = node.leftChildTree;
                while (temp.rightChildTree != null && temp.rightChildTree != temp){
                    temp = temp.rightChildTree;
                }
                if (temp.rightChildTree == null){
                    temp.rightChildTree = node;
                    node = node.leftChildTree;
                }else{
                    temp.rightChildTree = null;
                    System.out.println("node = [" + node.data + "]");
                    node = node.rightChildTree;
                }
            }

        }
    }
View Code

 

 

 

解決了這道題,我們再擴展一下,看一下一個二叉樹結構都應該有哪些功能呢?

基本操作:樹的主要操作有
(1)創建樹IntTree(&T)
(2)銷毀樹DestroyTree(&T)
(3)構造樹CreatTree(&T,deinition)
(4)置空樹ClearTree(&T)
(5)判空樹TreeEmpty(T)
(6)求樹的深度TreeDepth(T)
(7)獲得樹根Root(T)
(8)獲取結點Value(T,cur_e,&e),將樹中結點cur_e存入e單元中。
(9)數據賦值Assign(T,cur_e,value),將結點value,賦值於樹T的結點cur_e中。
(10)獲得雙親Parent(T,cur_e),返回樹T中結點cur_e的雙親結點。
(11)獲得最左孩子LeftChild(T,cur_e),返回樹T中結點cur_e的最左孩子。
(12)獲得右兄弟RightSibling(T,cur_e),返回樹T中結點cur_e的右兄弟。
(13)插入子樹InsertChild(&T,&p,i,c),將樹c插入到樹T中p指向結點的第i個子樹之前。
(14)刪除子樹DeleteChild(&T,&p,i),刪除樹T中p指向結點的第i個子樹。
(15)遍歷樹TraverseTree(T,visit())

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM