數據結構:樹形結構之樹及二叉樹

人生就是一場修行,修的是自己的心。修行,就是擴大自己的心量。心量越大,自己的舞臺就越大,能容的東西就越多。

什麼是樹形結構?

樹形結構是元素之間具有分支,且具有層次關係的結構, 其分支、分展的特徵類似
於自然界中的樹木。
在這裏插入圖片描述

樹的相關概念

樹的定義

樹是 N (N >= 0 )個結點的有限集合,N = 0 時,稱爲空樹,這是一種特殊情況。在任意一棵非空樹中應滿足:

1) 有且僅有一個特定的稱爲根的結點。

2)當 N > 1 時,其餘結點可分爲 m ( m > 0)個互不相交的有限集合T1 , T2 , T3 , … Tm ,其中每一個集合Ti本身又是一棵樹,並且稱爲根結點的子樹。

顯然樹的定義是遞歸的,是一種遞歸的數據結構。樹作爲一種邏輯結構,同時也是一種分層結構,具有以下兩個特點:

1)樹的根結點沒有前驅結點,除根結點之外的所有結點有且只有一個前驅結點。

2)樹中所有結點可以有零個或多個後繼結點。

二叉樹的定義

二叉樹 (Binany Tee)是n(n>=0)個結點的有限集合。當n=0時,稱爲空二叉樹:當n>0時,該集合由個根結點及兩棵互不相交的,被分別稱爲左子樹和右子樹的二又樹組成。以前面定義的樹爲基礎,二又樹可以理解爲是滿足以下兩個條件的樹形結構。
1) 每個結點的度不大於2。
2)結點每棵子樹的位置是明確區分左右的,不能隨意改變。

由上述定義可以看出:二叉樹中的每個結點只能有0、1 或2個孩子,而且孩子有左右之分,即使僅有一個孩子,也必須區分左右。位於左邊的孩子(或子樹)叫左孩子(左子樹),位於右邊的孩子(或子樹)叫右孩子(右子樹)。
在這裏插入圖片描述

二叉樹的鏈式存儲結構

二叉樹的存儲結構有順序存儲結構和鏈式存儲結構,順序存儲是按照對滿二叉樹的節點連續編號的次序,將二叉樹中編號爲i的及節點放入數組的第i個分量中。
在這裏插入圖片描述
這種存儲方式適合於滿二叉樹或完全二叉樹,並且很容易計算出左右孩子或雙親的下標位置,但對於一般的二叉樹來說,若二叉樹的深度爲k,則需要2^k個存儲單元,特別是單支的二叉樹,空間浪費極大。使用鏈式存儲可動態分配內存空間。

C語言實現二叉樹
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#define ERROR 0
#define OK 1
#define TRUE 1
#define FALSE 0
#define NOTFOUND -1
#define biTreeDataType char

typedef struct biTree{

    struct biTree *LChild;
    biTreeDataType data;
    struct biTree *RChild;

}BiTree;

//二叉樹的創建
void Create_BiTree(BiTree ** root){

    *root = (BiTree *) malloc(sizeof(BiTree));

    biTreeDataType elem;
    elem = getchar();

    if (elem == '#') {
        *root = NULL;
        return;
    } else {
        (*root)->data = elem;
        Create_BiTree(&((*root)->LChild));
        Create_BiTree(&((*root)->RChild));
    }
}

//數據訪問
Visit(biTreeDataType data){

    printf("%c", data);
}

//先序遍歷
void PrintPre_BiTree(BiTree *root) {

    if (root != NULL) {

        Visit(root->data);
        PrintPre_BiTree(root->LChild);
        PrintPre_BiTree(root->RChild);
    }
}

//中序遍歷
void PrintMid_BiTree(BiTree *root){

    if (root != NULL) {

        PrintPre_BiTree(root->LChild);
        Visit(root->data);
        PrintPre_BiTree(root->RChild);
    }

}

//後序遍歷
void PrintOrd_BiTree(BiTree *root){

    if (root != NULL) {

        PrintOrd_BiTree(root->LChild);
        PrintOrd_BiTree(root->RChild);
        Visit(root->data);
    }
}

//先序輸出樹的深度
void PrintPreAndGrade_BiTree(BiTree *root,int n) {

    if (root != NULL) {

        printf("%c %d\n", root->data, n);
        PrintPreAndGrade_BiTree(root->LChild,n+1);
        PrintPreAndGrade_BiTree(root->RChild,n+1);
    }
}

二分搜索樹

二分搜索樹( Binary Search Tree,BST),又稱爲二叉查找樹,是一種高效的數據結構。它是滿足以下性質的特殊二叉樹。
二叉排序樹或者是一棵空樹,或者是具有如下特性的二叉樹:
1)若它的左子樹不空,則左子樹上所有結點的值均小於根結點的值;
2)若它的右子樹不空,則右子樹上所有結點的值均大於根結點的值;
3)它的左、右子樹也都分別是二叉排序樹。
顯然,這是一個遞歸定義,首先要保證結點的值之間具有可比性,另外,關鍵字之間不允許有重複出現。
在這裏插入圖片描述

向二分搜索樹中插入元素

插入一個元素事實上和查找十分相似,只不過是查找這個元素插入的位置,通過遞歸的方式,我們可以先找到插入的位置,然後將元素插入,最後返回插入完成後節點的根節點,這樣逐層返回根節點。

	/**
     * 向二分搜索樹中插入元素
     *
     * @param elem
     */
    public void add(T elem) {
        root = add(root, elem);
        size++;
    }

    private Node add(Node parent, T e) {

        if (parent == null) {
            return new Node(e);
        }

        if (e.compareTo(parent.data) > 0) { //忽略重複元素
            parent.right = add(parent.right, e);
        } else if (e.compareTo(parent.data) < 0) {
            parent.left = add(parent.left, e);
        }

        return parent;
    }
刪除二分搜索樹中的元素

刪除元素相對複雜一些,刪除一個元素會出現以下三種刪除情況:

  1. 待刪除的節點是葉子結點
  2. 待刪除的節點只有左子樹或只有右子樹
  3. 待刪除節點既有左子樹又有右子樹
    雖然在刪除的時候會有以上三種刪除情況,但事實上其實只有兩種,因爲待刪除節點是葉子節點可以看成是隻有左子樹或只有右子樹的情況,因爲空樹也是二分搜索樹。

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
相信從上面幾張圖中發現,其實只有左子樹或只有右子樹的刪除是很簡單的:

  • 如果只有左子樹 ,那麼就將刪除結點的左子樹的根節點去替換待刪除節點的位置
  • 如果只有右子樹 ,那麼就將刪除結點的右子樹的根節點去替換待刪除節點的位置

那麼如何實現刪除左右子樹都存在的節點呢?下面介紹由Habbard提出的Habbard Deletion算法。

根據這個算法,要刪除左右子樹都存在的節點實質上是用待刪除節點的後繼節點去替換他的位置:
在這裏插入圖片描述
在這裏插入圖片描述
d元素的後繼爲59,根據二分搜索樹的特點,d節點的後繼其實解釋d節點右子樹中的最小值。
到此59所在的節點解釋s節點的後繼節點s,接下來需要將s節點替換d節點。
在這裏插入圖片描述
將s節點從待刪除結點d右子樹中刪除
在這裏插入圖片描述
然後讓s節點的左子樹和右子樹分別連接上d的左子樹和右子樹。
在這裏插入圖片描述
最後刪除d節點,s作爲新子樹的根返回。

至此就完成了左右子樹都存在的節點的刪除。

Java實現二分搜索樹
package cn.boom.tree;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class BiSearchTree<T extends Comparable<T>> {

    private Node root;
    private int size;


    private class Node {

        private T data;
        private Node left;
        private Node right;

        public Node() {
            this.data = null;
            this.left = null;
            this.right = null;
        }

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

    public BiSearchTree() {
        root = null;
        size = 0;
    }

    /**
     * 獲取樹中元素個數
     *
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 樹是否爲空
     *
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 向二分搜索樹中插入元素
     *
     * @param elem
     */
    public void add(T elem) {

        root = add(root, elem);
        size++;
    }

    private Node add(Node parent, T e) {

        if (parent == null) {
            return new Node(e);
        }

        if (e.compareTo(parent.data) > 0) { //忽略重複元素
            parent.right = add(parent.right, e);
        } else if (e.compareTo(parent.data) < 0) {
            parent.left = add(parent.left, e);
        }

        return parent;
    }

    /**
     * 元素e是否存在
     *
     * @param e
     * @return
     */
    public boolean contains(T e) {
        return contains(root, e);
    }

    private boolean contains(Node parent, T e) {

        if (parent != null) {

            if (e.compareTo(parent.data) == 0) {
                return true;
            } else if (e.compareTo(parent.data) > 0) {
                return contains(parent.right, e);
            } else if (e.compareTo(parent.data) < 0) {
                return contains(parent.left, e);
            }
        }
        return false;
    }

    private void visit(T elem) {
        System.out.print(elem + " ");
    }

    //先序遞歸遍歷
    public void preOrder() {
        preOrder(root);
    }

    private void preOrder(Node parent) {

        if (parent != null) {

            visit(parent.data);
            preOrder(parent.left);
            preOrder(parent.right);
        }
    }

    //先序非遞歸遍歷
    public void preOrderNR() {

        Stack<Node> stack = new Stack<Node>();
        stack.push(root);

        while (!stack.isEmpty()) {

            Node node = stack.pop();
            visit(node.data);

            if (node.right != null) {
                stack.push(node.right);
            }

            if (node.left != null) {
                stack.push(node.left);
            }
        }

    }

    //中序遞歸遍歷
    public void inOrder() {

        inOrder(root);
    }

    private void inOrder(Node parent) {

        if (parent != null) {

            inOrder(parent.left);
            visit(parent.data);
            inOrder(parent.right);
        }
    }

    //後序遞歸遍歷
    public void postOrder() {

        postOrder(root);
    }

    private void postOrder(Node parent) {

        if (parent != null) {

            postOrder(parent.left);
            postOrder(parent.right);
            visit(parent.data);
        }
    }

    //層次遍歷
    public void levelOrder() {

        Queue<Node> queue = new LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {

            Node node = queue.remove();
            visit(node.data);

            if (node.left != null) {
                queue.add(node.left);
            }

            if (node.right != null) {
                queue.add(node.right);
            }
        }
    }

    /**
     * 找最小值
     *
     * @return
     */
    public T minMum() {

        return minMum(root).data;
    }

    private Node minMum(Node parent) {

        if (parent == null) {
            throw new IllegalArgumentException(" BST is empty !");
        }

        if (parent.left == null) {
            return parent;
        } else {
            return minMum(parent.left);
        }
    }


    /**
     * 找最大值
     *
     * @return
     */
    public T maxMum() {

        return maxMum(root).data;
    }

    private Node maxMum(Node parent) {

        if (parent == null) {
            throw new IllegalArgumentException(" BST is empty !");
        }

        if (parent.right == null) {
            return parent;
        } else {
            return maxMum(parent.right);
        }
    }

    /**
     * 刪除最小元素
     *
     * @return
     */
    public T removeMin() {

        T minElem = minMum();
        root = removeMin(root);
        return minElem;
    }

    /**
     * 返回刪除結點後的根節點
     *
     * @param parent
     * @return
     */
    private Node removeMin(Node parent) {

        if (parent == null) {
            throw new IllegalArgumentException(" BST is empty !");
        }

        if (parent.left == null) {

            Node rightNode = parent.right;
            parent.right = null;
            size--;
            return rightNode;

        } else {

            parent.left = removeMin(parent.left);
            return parent;
        }
    }

    /**
     * 刪除最大元素
     *
     * @return
     */
    public T removeMax() {

        T elem = maxMum();
        root = removeMax(root);
        return elem;
    }

    /**
     * 遞歸刪除最大元素,返回根節點
     *
     * @param parent
     * @return
     */
    private Node removeMax(Node parent) {

        if (parent == null) {
            throw new IllegalArgumentException(" BST is empty !");
        }

        if (parent.right == null) {

            Node leftNode = parent.left;
            parent.left = null;
            size--;
            return leftNode;

        } else {

            parent.right = removeMax(parent.right);
            return parent;
        }
    }


    /**
     * 刪除元素e
     *
     * @param e
     */
    public void remove(T e) {
        root = remove(root, e);
    }


    public Node remove(Node parent, T e) {

        if (parent == null) {
            return null;
        }

        if (e.compareTo(parent.data) > 0) {

            parent.right = remove(parent.right, e);
            return parent;
        } else if (e.compareTo(parent.data) < 0) {

            parent.left = remove(parent.left, e);
            return parent;
        } else {

            if (parent.left == null) {

                Node rightNode = parent.right;
                parent.right = null;
                size--;
                return rightNode;
            }

            if (parent.right == null) {

                Node leftNode = parent.left;
                parent.left = null;
                size--;
                return leftNode;
            }

            //左右子樹均不爲空
            //從右子樹中得到比待刪結點大的最小元素結點,並用此節點頂替刪除結點
            Node replaceNode = minMum(parent.right);//removeMin中size--,因此不需要在維護size了

            replaceNode.right = removeMin(parent.right);
            replaceNode.left = parent.left;
            parent.left = parent.right = null;

            return replaceNode;
        }
    }
}

參考文獻

[1]王曙燕.數據結構與算法:人民郵電出版社
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章