1. 程式人生 > >數據結構7 二叉樹

數據結構7 二叉樹

dir col .cn 增加 二叉樹 tree 資源 java 使用

這篇文章開始總結 樹和二叉樹。

什麽是樹呢?

1、樹的定義

(1)有且僅有一個特定的稱為根(root) 的節點。

(2)當 n>1 時,其余節點可分為 m(m>0) 個互不相交的集合。其中每個集合本身又是一個棵樹,並稱為根的子樹。

2、樹的表示方法

最常見的是 樹形表示法 和 廣義表表示法,下面是樹形表示法,如圖所示。

技術分享圖片

上圖的 廣義表表示法為:(A(B(D,E),C(F,G)))

3、常見的術語

  (1)父節點,孩子節點,兄弟節點。以上圖為例,A是B和C的父節點,B和C是A的孩子節點,B和C之間互為兄弟節點。

  (2)節點的度和樹的度。節點的度即節點有幾個分支,比如節點A有兩個分支B和C,那麽節點A的度就是2,樹的度即為一棵樹中節點的最大度數,所以上圖的樹的度也是2。

  (3)有序樹和無序樹。如果將樹中節點的子樹看成是從左至右依次有序且不能交換,則稱該樹為有序樹,否則稱為無序樹。

  (4)森林。在上圖中,如果將根節點A拿掉,那麽B子樹和C子樹合並就是森林了。

  (5)二叉樹。二叉樹是一種特殊的樹。它的每個節點最多只有兩棵子樹。

4、二叉樹的常見性質

  性質1:在二叉樹的第 i 層上最多有 2i-1 個節點 (i>=1)

  性質2:深度為 k 的二叉樹最多有 2k-1 個節點 (k>=1)

  性質3:滿二叉樹:一棵深度為 k 且有 2k-1 個節點的二叉樹稱為 滿二叉樹。

      完全二叉樹:一棵深度為 k 的二叉樹,其前 k-1 層是一棵滿二叉樹,而最下面一層(即第k層) 上的節點都集中在該層最左邊

的若幹位置上。這樣的二叉樹稱為 完全二叉樹。

滿二叉樹一定是完全二叉樹,但完全二叉樹不一定是滿二叉樹。

下面是 滿二叉樹 和 完全二叉樹 的圖示:

技術分享圖片

技術分享圖片

5、二叉樹的兩種存儲結構

5-1:順序存儲

對於完全二叉樹而言,可以使用順序存儲結構。但對於一般的二叉樹而言,使用順序存儲結構會有兩個缺點:一、如果不是完全二叉樹,則必須將其轉化為完全二叉樹;二、增加了很多虛節點,浪費資源空間。

技術分享圖片

5-2:鏈式存儲

鏈式存儲是最常用的一種二叉樹存儲結構。每個節點設置三個域,分別是值域、左指針域和右指針域,用 data 表示值域,lchild 和 rchild 分別表示指向左子樹、右子樹的指針域。如下圖:

技術分享圖片

6、二叉樹的常見操作

6-1:插入節點

思路:首先找到要插入節點的父節點,然後確定插入到父節點的左邊還是右邊,最後將節點插入。

6-2:查找節點

思路:運用遞歸查找。

6-3:計算樹的深度

思路:分別遞歸左子樹和右子樹,取長度較大的那一個作為整個樹的深度。

6-4:遍歷之 先序遍歷

思路:先訪問根節點,然後遍歷左子樹,再遍歷右子樹

6-5:遍歷之 中序遍歷

思路:先遍歷左子樹,再訪問根節點,最後遍歷右子樹

6-6:遍歷之 後序遍歷

思路:先遍歷左子樹,再遍歷右子樹,最後訪問根節點

6-7:遍歷之 按層遍歷

思路:從上到下,從左到右遍歷節點

7、二叉樹的常見操作的代碼實現

代碼如下:

節點類Node.java

public class Node {
    public int data; // 數據域
    public Node left; // 左孩子
    public Node right; // 右孩子
}

BinTree.java

import java.util.Scanner;

public class BinTree {
    // 按層遍歷的存儲空間長度
    public static int length;

    /**
     * 生成根節點
     */
    public static Node createRoot() {
        Node root = new Node();
        System.out.println("請輸入根節點,以便生成樹:");
        root.data = new Scanner(System.in).nextInt();
        System.out.println("根節點生成成功");
        return root;
    }

    /**
     * 插入節點
     *
     * @param root 二叉樹的根節點
     * @return 返回二叉樹的根節點
     * @throws Exception
     */
    public static Node insert(Node root) throws Exception {
        while (true) {
            System.out.println("請輸入待插入節點的數據:");
            Node node = new Node();
            node.data = new Scanner(System.in).nextInt();

            // 獲取父節點數據
            System.out.println("請輸入它(待插入節點) 的父節點:");
            int parentNodeData = new Scanner(System.in).nextInt();

            // 確定插入方向
            System.out.println("請確定要插入到父節點的:1 左側, 2 右側");
            int direction = new Scanner(System.in).nextInt();

            // 插入節點
            root = insertNode(root, node, parentNodeData, direction);

            if (root == null) {
                System.out.println("未找到父節點,請重新輸入!");
                continue;
            }
            System.out.println("插入成功,是否繼續? 1繼續,2退出");

            if (new Scanner(System.in).nextInt() == 1)
                continue;
            else
                break; // 退出循環
        }
        return root;
    }

    /**
     * 從insert()方法抽取出來的需要遞歸的部分
     *
     * @param root           二叉樹的根節點
     * @param node           待插入節點
     * @param parentNodeData 待插入節點的父節點
     * @param direction      插入到左邊還是右邊
     * @return 返回二叉樹的根節點
     * @throws Exception
     */
    public static Node insertNode(Node root, Node node, int parentNodeData, int direction) throws Exception {
        if (root == null)
            return null;

        // 找到父節點
        if (root.data == parentNodeData) {
            switch (direction) {
                case 1:
                    if (root.left != null)
                        throw new Exception("左節點已存在,不能插入!");
                    else
                        root.left = node;
                    break;
                case 2:
                    if (root.right != null)
                        throw new Exception("右節點已存在,不能插入!");
                    else
                        root.right = node;
                    break;
            }
        }

        // 向左子樹查找父節點(遞歸)
        insertNode(root.left, node, parentNodeData, direction);
        // 向右子樹查找父節點(遞歸)
        insertNode(root.right, node, parentNodeData, direction);

        return root;
    }

    /**
     * 查找節點
     */
    public static boolean getNode(Node root, int data) {
        if (root == null)
            return false;

        // 查找成功
        if (root.data == data)
            return true;

        // 遞歸查找
        boolean result1 = getNode(root.left, data);
        boolean result2 = getNode(root.right, data);
        return result1 || result2;
    }

    /**
     * 獲取二叉樹的深度
     * 思路:分別遞歸左子樹和右子樹,取長度較大的那一個作為整個樹的深度
     *
     * @param root 二叉樹的根節點
     * @return
     */
    public static int getLength(Node root) {
        if (root == null)
            return 0;
        int leftLength, rightLength;

        // 遞歸左子樹的深度
        leftLength = getLength(root.left);
        // 遞歸右子樹的深度
        rightLength = getLength(root.right);

        if (leftLength > rightLength)
            return leftLength + 1;
        else
            return rightLength + 1;
    }

    /**
     * 先序遍歷
     * 思路:先訪問根節點,然後遍歷左子樹,再遍歷右子樹
     *
     * @param root 根節點
     */
    public static void DLR(Node root) {
        if (root == null)
            return;

        // 輸出節點的值
        System.out.print(root.data + " ");

        // 遞歸遍歷左子樹
        DLR(root.left);

        // 遞歸遍歷右子樹
        DLR(root.right);
    }

    /**
     * 中序遍歷
     * 思路:先遍歷左子樹,再訪問根節點,最後遍歷右子樹
     *
     * @param root 根節點
     */
    public static void LDR(Node root) {
        if (root == null)
            return;

        // 遍歷左子樹
        LDR(root.left);

        // 輸出節點的值
        System.out.print(root.data + " ");

        // 遍歷右子樹
        LDR(root.right);
    }

    /**
     * 後序遍歷
     * 思路:先遍歷左子樹,再遍歷右子樹,最後訪問根節點
     *
     * @param root 根節點
     */
    public static void LRD(Node root) {
        if (root == null)
            return;

        // 遍歷左子樹
        LRD(root.left);

        // 遍歷右子樹
        LRD(root.right);

        // 輸出節點的值
        System.out.print(root.data + " ");
    }

    /**
     * 按層遍歷
     * 思路:從上到下,從左到右遍歷節點
     *
     * @param root 根節點
     */
    public static void traversalLevel(Node root) {
        if (root == null)
            return;

        int head = 0;
        int tail = 0;

        // 申請保存空間
        Node[] nodeList = new Node[length];

        // 將當前二叉樹保存到數組中
        nodeList[tail] = root;

        // 計算tail的位置
        tail = (tail + 1) % length; // 除留余數法

        while (head != tail) {
            Node tempNode = nodeList[head];

            // 計算head的位置
            head = (head + 1) % length;

            // 輸出節點的值
            System.out.print(tempNode.data + " ");

            // 如果左子樹不為空,則將左子樹保存到數組的tail位置
            if (tempNode.left != null) {
                nodeList[tail] = tempNode.left;
                // 重新計算tail的位置
                tail = (tail + 1) % length;
            }

            // 如果右子樹不為空,則將右子樹保存到數組的tail位置
            if (tempNode.right != null) {
                nodeList[tail] = tempNode.right;
                // 重新計算tail的位置
                tail = (tail + 1) % length;
            }
        }
    }
}

BinTreeTest.java

public class BinTreeTest {
    public static void main(String[] args) throws Exception {
        System.out.println("*******鏈式存儲的二叉樹*******");
        // 創建根節點
        Node root = BinTree.createRoot();

        // 1、插入節點
        System.out.println("********插入節點*******");
        BinTree.insert(root);

        // 2、查找節點
        System.out.print("查找data為2的節點是否存在:");
        boolean result = BinTree.getNode(root, 2);
        System.out.println(result);

        // 3、獲取二叉樹的深度
        System.out.println("當前二叉樹的深度為:" + BinTree.getLength(root));

        // 4、先序遍歷
        System.out.print("先序遍歷:");
        BinTree.DLR(root);
        System.out.println("");

        // 5、中序遍歷
        System.out.print("中序遍歷:");
        BinTree.LDR(root);
        System.out.println("");

        // 6、後序遍歷
        System.out.print("後序遍歷:");
        BinTree.LRD(root);
        System.out.println("");

        // 7、按層遍歷
        System.out.print("按層遍歷:");
        BinTree.length = 100;
        BinTree.traversalLevel(root);
        System.out.println("");
    }
}

運行結果:

技術分享圖片

歡迎轉載,但請保留文章原始出處

本文地址:http://www.cnblogs.com/nnngu/p/8308220.html

數據結構7 二叉樹