Java數據結構:樹基礎及二叉樹

1. 樹的簡介

1.1 樹的定義

樹是一種特殊的數據結構,它可以用來描述有分支的結構,是由一個或一個以上的節點所組成的有限集合。

1.2 樹的特點

a. 存在一個特殊的節點,稱爲根節點;

b. 沒有父節點的節點稱爲根節點;

c. 每一個非根節點有且只有一個父節點;

d. 除了根節點外,每個子節點可以分爲多個不相交的子樹;

圖中A節點就稱爲根節點,B、C、D 均爲A的子節點。

1.3 樹的專有名詞

a. 樹根或者根節點(root):沒有父節點的節點稱爲根節點,如下圖中樹的根節點爲 A。

b. 父節點(parent):每一個節點的上層節點爲父節點,如下圖中 B 的父節點爲 A,E 的父節點爲 C。

c. 子節點(children):每一個節點的下層節點爲子節點,如 A的子節點有 B、C、D,而 C 的子節點有 E、F、G。

d. 兄弟節點(siblings):有共同父節點的節點爲兄弟節點,如 B、C、D 的父節點均爲 A,所以彼此稱爲兄弟節點。

e. 度(degree):子樹的個數稱爲該節點的度,如 A的度爲3,D 的度爲1。

f. 葉子節點或終端節點(terminal node):沒有子節點的節點,即度爲 0 的節點,如圖中的 B、E、F、G、I。

g. 階層或級(level):根節點的階層爲 1,圖中 A的階層爲 1,B、C、D的階層爲 2,E、F、G、H的階層爲3,I 的階層爲4。

h. 高度(height):樹的最大階層,例如此樹的高度爲 4。

i. 森林(forest):森林是由 n 個不相交的樹組成,移去樹根即爲森林。如圖中,移除節點 A,則包含三個樹的森林。

2. 二叉樹

2.1 二叉樹的定義

二叉樹是由有限個節點所組成的集合,此集合可以爲空集合,或由一個樹根及其左右兩個子樹所組成。簡單的說,二叉樹最多隻能有兩個子節點,就是度小於或者等於2。二叉樹的數據結構如下:

2.2 二叉樹的性質

1. 二叉樹第 i 層上的節點數目最多爲 2^{i-1},其中 i ≥ 1。

2. 深度爲 k 的二叉樹至多有 2^{k}-1個節點,其中 k ≥ 1。

2.3. 滿二叉樹

定義

高度爲 h,並且由 2^h - 1 個節點的二叉樹,被稱爲滿二叉樹。如下圖所示,這是一棵滿二叉樹

2.4. 完全二叉樹

定義

一棵二叉樹中,只有最下層節點的度可以小於2,並且最下層的葉節點集中在靠左的若干位置上,這樣的二叉樹稱爲完全二叉樹。

特點

葉子節點只能出現在最下層和次下層,且最下層的葉子節點集中在樹的左部。顯然,一棵滿二叉樹必定是一棵完全二叉樹,而完全二叉樹未必是滿二叉樹。如下圖,這是一棵完全二叉樹:

而下圖不是一棵完全二叉樹:

2.5 二叉查找樹

定義

二叉查找樹,又稱爲二叉搜索樹。假設 x 爲二叉查找樹中的一個節點,x 節點包含關鍵字 key,節點的 key 值記爲 key[x]。如果 y 是 x 的左子樹中的一個節點,則 key[y] ≤ key[x] ;如果 y 是 x 的右子樹的一個節點,則 key[y] ≥ key[x]。如下圖所示:

特點

a. 每個樹根的值需大於左子樹的值。

b. 每一個樹根的值需小於右子樹的值。

c. 左右子樹也是二叉搜索樹。

d. 沒有鍵值相等的節點。

3. 二叉樹存儲

3.1 數組表示法

如果要使用一維數組來存儲二叉樹,首先將二叉樹想象成一個滿二叉樹,而且第 k 個階層具有 2^{k-1} 個節點,並且按照順序存放在此一維數組之中。這裏從數組下標爲1的位置開始存放。

如下圖所示的二叉樹,用數組表示爲:{0,13,65,5,97,25,0,37,22,0,4,28,0,0,32,0}。

3.2 鏈表表示法

樹的節點:

package zzw.cn.tree;

/**
 * @author 鷺島猥瑣男
 * @create 2019/8/24 21:37
 */
public class TreeNode
{
    int value;//節點值
    TreeNode leftNode;//指向左節點
    TreeNode rightNode;//指向右節點

    public TreeNode(int value)
    {
        this.value = value;
    }
}

二叉樹:

package zzw.cn.tree;

/**
 * @author 鷺島猥瑣男
 * @create 2019/8/25 0:40
 */
public class BinaryTree
{
    TreeNode root;//根節點

    public BinaryTree(int[] array)
    {
        root = transformArrayToBinaryTree(array, 1);
    }

    //採用遞歸方式創建二叉樹
    public TreeNode transformArrayToBinaryTree(int[] array, int index)
    {
        if (index < array.length)
        {
            int value = array[index];
            if (value != 0)
            {
                TreeNode node = new TreeNode(value);
                array[index] = 0;
                node.leftNode = transformArrayToBinaryTree(array, index * 2);
                node.rightNode = transformArrayToBinaryTree(array, index * 2 + 1);
                return node;
            }
        }
        return null;
    }
}

4. 二叉樹的遍歷

二叉樹的遍歷,就是訪問樹的所有節點各一次,並且在遍歷後,將樹中的數據轉化爲線性關係。樹的遍歷存在三種方式:

a. 中序遍歷

b. 前序遍歷

c. 後續遍歷

4.1 中序遍歷

若二叉樹非空,則執行以下操作:左子樹 → 樹根 → 右子樹。就是沿着樹的左子樹一直往下,直到無法前進後退回返回父節點,再往右子樹一直往下。如果右子樹也走完了就退回上層的左節點,再重複左、中、右的順序遍歷。

上圖樹的中序遍歷如下所示: 

package zzw.cn.tree;

/**
 * @author 鷺島猥瑣男
 * @create 2019/8/25 1:18
 */
public class BinaryTreeTest
{
    public static void main(String[] args)
    {
        int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
        BinaryTree tree = new BinaryTree(arr);
        inOrder(tree);//中序遍歷
    }

    //中序遍歷
    private static void inOrder(TreeNode node)
    {
        if (node != null)
        {
            inOrder(node.leftNode);
            System.out.print(node.value + " ");
            inOrder(node.rightNode);
        }
    }

    private static void inOrder(BinaryTree tree)
    {
        System.out.print("中序遍歷:");
        inOrder(tree.root);
    }
}

結果:

中序遍歷:22 97 65 4 25 28 13 5 32 37 

4.2 前序遍歷

前序遍歷的順序爲:樹根 → 左子樹 → 右子樹。前序遍歷就是從根節點開始處理,根節點處理完往左子樹,直到無法前進再處理右子樹。

上圖樹的前序遍歷如下所示: 

package zzw.cn.tree;

/**
 * @author 鷺島猥瑣男
 * @create 2019/8/25 1:18
 */
public class BinaryTreeTest
{
    public static void main(String[] args)
    {
        int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
        BinaryTree tree = new BinaryTree(arr);
        preOrder(tree);//前序遍歷
    }

    //前序遍歷
    private static void preOrder(TreeNode node)
    {
        if (node != null)
        {
            System.out.print(node.value + " ");
            preOrder(node.leftNode);
            preOrder(node.rightNode);
        }
    }

    private static void preOrder(BinaryTree tree)
    {
        System.out.print("前序遍歷:");
        preOrder(tree.root);
    }
}

結果:

前序遍歷:13 65 97 22 25 4 28 5 37 32 

4.3 後序遍歷

後序遍歷的順序爲:左子樹 → 右子樹 → 樹根。後序遍歷和前序遍歷的方法相反,它是把左子樹的節點和右節點都處理完了才處理樹根。

上圖樹的後序遍歷如下所示: 

package zzw.cn.tree;

/**
 * @author 鷺島猥瑣男
 * @create 2019/8/25 1:18
 */
public class BinaryTreeTest
{
    public static void main(String[] args)
    {
        int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
        BinaryTree tree = new BinaryTree(arr);
        postOrder(tree);//後序遍歷
    }

    //後序遍歷
    private static void postOrder(TreeNode node)
    {
        if (node != null)
        {
            postOrder(node.leftNode);
            postOrder(node.rightNode);
            System.out.print(node.value + " ");
        }
    }

    private static void postOrder(BinaryTree tree)
    {
        System.out.print("後序遍歷:");
        postOrder(tree.root);
    }
}

結果:

後序遍歷:22 97 4 28 25 65 32 37 5 13 

參考:

https://github.com/LRH1993/android_interview/blob/master/data-structure/tree/tree-introduction.md

《圖解數據結構-使用Java》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章