學習筆記——二叉樹相關算法的實現(Java語言版)


二叉樹遍歷概念和算法

遍歷(Traverse):

  所謂遍歷(Traversal)是指沿着某條搜索路線,依次對樹中每個結點均做一次且僅做一次訪問。

 

  從二叉樹的遞歸定義可知,一棵非空的二叉樹由根結點及左、右子樹這三個基本部分組成。

  因此,在任一給定結點上,可以按某種次序執行三個操作:

    ⑴ 訪問結點本身(D),
    ⑵ 遍歷該結點的左子樹(L),
    ⑶ 遍歷該結點的右子樹(R)。
 
  先序/根遍歷DLR:根   左子樹     右子樹
  中序/根遍歷LDR:左子樹   根     右子樹
  后根/序遍歷LRD:左子樹     右子樹  根
 
   注意:由於樹的遞歸定義,其實對三種遍歷的概念其實也是一個遞歸的描述過程
 

算法實現:

二叉樹結點類

 

/**
 * 二叉鏈表的結點
 * @author shangyang
 *
 */
public class Node {
    
    Object value;        // 結點值
    Node leftChild;        // 左子樹的引用
    Node rightChild;    // 右子樹的引用
    
    public Node(Object value) {
        super();
        this.value = value;
    }

    public Node(Object value, Node leftChild, Node rightChild) {
        super();
        this.value = value;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
    }

    @Override
    public String toString() {
        return "Node [value=" + value + ", leftChild=" + leftChild + ", rightChild=" + rightChild + "]";
    }
}

 二叉樹方法接口類

/**
 * 二叉樹的接口
 * 可以有不同的實現類,每個類可以使用不同的存儲結構,比如順序結構、鏈式結構
 * @author shangyang
 *
 */
public interface BinaryTree {

    /**
     * 是否為空樹
     */
    public boolean isEmpty();
    
    /**
     * 樹結點數量
     */
    public int size();
    
    /**
     * 獲取樹的高度
     */
    public int getHeight();
    
    /**
     * 查詢指定值的結點
     * @param value
     * @return
     */
    public Node findKey(Object value);
    
    /**
     * 前序遞歸遍歷
     */
    public void preOrderTraverse();

    /**
     * 中序遞歸遍歷
     */
    public void inOrderTraverse();

    /**
     * 后序遞歸遍歷
     */
    public void postOrderTraverse();

    /**
     * 按照層次遍歷(借助隊列)
     */
    public void levelOrderByStack();

    /**
     * 中序非遞歸遍歷
     */
    public void inOrderByStack();
}
二叉樹接口類

實現二叉樹接口類

創建樹的根對象,並寫出構造函數。

public class LinkedBinaryTree implements BinaryTree {

    private Node root;    // 根結點
    
    public LinkedBinaryTree() {
    }

    public LinkedBinaryTree(Node root) {
        this.root = root;
    }
}

 創建二叉樹

    // 創建一個二叉樹
    Node nodeF = new Node("F",null,null);
    Node nodeE = new Node("E",null,null);
    Node nodeD = new Node("D",null,null);
    Node nodeC = new Node("C",nodeF,null);
    Node nodeB = new Node("B",nodeD,nodeE);
    Node nodeA = new Node("A",nodeB,nodeC);

    // 聲明nodeA為根結點
    BinaryTree btree = new LinkedBinaryTree(nodeA);        

 判斷二叉樹是否為空

為空返回true,不為空返回false

public boolean isEmpty() {
    return root == null;
}

輸出二叉樹結點數量

運用遞歸的思想,二叉樹結點樹 = 左子樹結點數量 + 右子樹結點數量 + 1

  public int size() {
        System.out.println("二叉樹結點數量: ");
        return this.size(root);
    }
    private int size(Node root) {
        if(root == null) {
            return 0;
        } else {
            // 獲取左子樹的數量
            int nl = this.size(root.leftChild);
            // 獲取右子樹的數量
            int nr = this.size(root.rightChild);
            // 返回左子樹、右子樹size之和並加1
            return nl + nr + 1;
        }
    }

二叉樹的深度(高度)

如果二叉樹為空,則其深度為0。

如果二叉樹只有根結點,無左右子樹,則其深度為1。

如果二叉樹結點數大於1,則用遞歸的思想計算其深度。二叉樹的深度 = 左右子樹的最大深度 + 1。

    public int getHeight() {
        System.out.println("二叉樹的高度是 :");
        return this.getHeight(root);
    }
    private int getHeight(Node root) {
        if(root == null) {
            return 0;
        } else {
            // 獲取左子樹的高度
            int nl = this.getHeight(root.leftChild);
            // 獲取右子樹的高度
            int nr = this.getHeight(root.rightChild);
            // 返回左子樹、右子樹較大高度並加1
            return nl > nr ? nl + 1 : nr + 1;
        }
    }

在二叉樹中查找某個值

運用遞歸的思想,將要查找的值逐個與根結點,根結點的左子樹和右子樹的值進行比較,並進行返回。

    public Node findKey(Object value) {
        return this.findKey(value,root);
    }
    private Node findKey(Object value,Node root) {
        // 結點為空,可能是整個樹的根結點,也可能是遞歸調用中葉子結點中左孩子和右孩子
        if(root == null) {
            return null;
        } else if (root != null && root.value == value) {
            return root;
        } else {    // 遞歸體
            Node leftnode = this.findKey(value,root.leftChild);
            Node rightnode = this.findKey(value, root.rightChild);
            if(leftnode != null && leftnode.value == value) {
                return leftnode;
            } else if (rightnode != null && rightnode.value == value) {
                return rightnode;
            } else {
                return null;
            }
        }
    }

先序遞歸遍歷

若二叉樹非空,則依次執行如下操作:
  ⑴ 訪問根結點;
  ⑵ 遍歷左子樹;
  ⑶ 遍歷右子樹。
    public void preOrderTraverse() {
        // 輸出根結點的值
        if(root != null) {
            System.out.print(root.value + "  ");
            
            // 對左子樹進行先序遍歷
            // 構建一個二叉樹,根是左子樹的根
            BinaryTree leftTree = new LinkedBinaryTree(root.leftChild);
            leftTree.preOrderTraverse();
            
            // 對右子樹進行先序遍歷
            // 構建一個二叉樹,根是左子樹的根
            BinaryTree rightTree = new LinkedBinaryTree(root.rightChild);
            rightTree.preOrderTraverse();
        }
    }

中序遞歸遍歷

若二叉樹非空,則依次執行如下操作:
  ⑴ 遍歷左子樹;
  ⑵ 訪問根結點;
  ⑶ 遍歷右子樹。
    public void inOrderTraverse() {
        System.out.println("中序遍歷");
        this.inOrderTraverse(root);
        System.out.println();
    }
    private void inOrderTraverse(Node root) {
        if(root != null) {
            // 遍歷左子樹
            this.inOrderTraverse(root.leftChild);
            // 輸出根的值
            System.out.print(root.value + "  ");
            // 遍歷右子樹
            this.inOrderTraverse(root.rightChild);
        }
    }

 后續遞歸遍歷

若二叉樹非空,則依次執行如下操作:
  ⑴ 遍歷左子樹;
  ⑵ 遍歷右子樹;
  ⑶ 訪問根結點。
    public void postOrderTraverse() {
        System.out.println("后序遍歷");
        this.postOrderTraverse(root);
        System.out.println();
    }
    private void postOrderTraverse(Node root) {
        if(root != null) {
            // 遍歷左子樹
            this.postOrderTraverse(root.leftChild);
            // 遍歷右子樹
            this.postOrderTraverse(root.rightChild);
            // 輸出根的值
            System.out.print(root.value + "  ");
        }
    }

 按照層次遍歷(借助隊列)

按照從上到下、從左到右的次序進行遍歷。先遍歷完一層,再遍歷下一層,因此又叫廣度優先遍歷。

該方法可以借助java中提供queue隊列接口來完成。LinkedList實現了該接口。

    public void levelOrderByStack() {
        if(root == null) 
            return;
        Queue<Node> queue = new LinkedList<Node>();
        queue.add(root);
        while(queue.size() != 0) {
            int len = queue.size();
            for(int i = 0; i < len; i++) {
                Node temp = queue.poll();
                System.out.print(temp.value + "  ");
                if(temp.leftChild != null)
                    queue.add(temp.leftChild);
                if(temp.rightChild != null) 
                    queue.add(temp.rightChild);
            }
        }
    }

 

 中序非遞歸遍歷(借助棧)

(1) 若根結點不為空,則將其放如棧中,並判斷其左子樹是否為空。

(2) 若不為空,則將子樹根結點放入棧中,並繼續向下判斷,直至左子樹為空。

(3) 若棧中有結點,則將其取出,並對其右子樹根結點進行(1)(2)步驟,直至無結點或棧中元素為空。

    public void inOrderByStack() {
        // 創建棧
        Deque<Node> stack = new LinkedList<Node>();
        Node current = root;
        while(current != null || !stack.isEmpty()) {
            while(current != null) {
                stack.push(current);
                current = current.leftChild;
            }
            
            if(!stack.isEmpty()) {
                current = stack.pop();
                System.out.print(current.value + " ");
                current = current.rightChild;
            }
        }
    }

 


免責聲明!

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



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