《戀上數據結構第1季》二叉樹代碼實現

數據結構與算法筆記目錄《戀上數據結構》 筆記目錄

想加深 Java 基礎推薦看這個Java 強化筆記目錄

我們實現一個 通用二叉樹(BinaryTree.java),裏面包含所有二叉樹類通用的代碼。後面學到的 二叉搜索樹AVL樹紅黑樹 都需要繼承這個 通用二叉樹

這一篇是代碼實現,先了解 二叉樹基礎知識

BinaryTree 基礎

package com.mj.tree;

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

/**
 * 二叉樹
 */
@SuppressWarnings("unchecked")
public class BinaryTree<E> {
    protected int size; // 元素數量
    protected Node<E> root; // 根節點

    /**
     * 訪問器接口 ——> 訪問器抽象類
     * 增強遍歷接口
     */
	/*public static interface Visitor<E>{
		void visit(E element);
	}*/
    public static abstract class Visitor<E> {
        boolean stop;
        // 如果返回true,就代表停止遍歷
        public abstract boolean visit(E element);
    }

    /**
     * 內部類,節點類
     */
    public static class Node<E> {
        E element;      // 元素值
        Node<E> left;   // 左節點
        Node<E> right;  // 右節點
        Node<E> parent; // 父節點

        public Node(E element, Node<E> parent) {
            this.element = element;
            this.parent = parent;
        }

        public boolean isLeaf() { // 是否葉子節點
            return left == null && right == null;
        }

        public boolean hasTwoChildren() { // 是否有兩個子節點
            return left != null && right != null;
        }
    }
    
    /**
     * 元素的數量
     */
    public int size() {
        return size;
    }

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

    /**
     * 清空所有的元素
     */
    public void clear() {
        root = null;
        size = 0;
    }

}

遍歷(先序、中序、後序、層次遍歷)

先序遍歷: preorder()

/**
 * 前序遍歷(遞歸)
 */
public void preorder(Visitor<E> visitor) {
    if (visitor == null) return;
    preorder(root, visitor);
}
public void preorder(Node<E> node, Visitor<E> visitor) {
    if (node == null || visitor.stop) return;
    // 根
    visitor.stop = visitor.visit(node.element);
    // 左
    preorder(node.left, visitor);
    // 右
    preorder(node.right, visitor);
}

中序遍歷: inorder()

/**
 * 中序遍歷(遞歸)
 */
public void inorder(Visitor<E> visitor) {
    if (visitor == null) return;
    inorder(root, visitor);
}
public void inorder(Node<E> node, Visitor<E> visitor) {
    if (node == null || visitor.stop) return;
    // 左
    inorder(node.left, visitor);
    // 根
    if (visitor.stop) return;
    visitor.stop = visitor.visit(node.element);
    // 右
    inorder(node.right, visitor);
}

後序遍歷: postorder()

/**
 * 後序遍歷(遞歸)
 */
public void postorder(Visitor<E> visitor) {
    if (visitor == null) return;
    postorder(root, visitor);
}
public void postorder(Node<E> node, Visitor<E> visitor) {
    if (node == null || visitor.stop) return;
    // 左
    postorder(node.left, visitor);
    // 右
    postorder(node.right, visitor);
    // 根
    if (visitor.stop) return;
    visitor.stop = visitor.visit(node.element);
}

層次遍歷: levelOrder()

/**
 * 層次遍歷(隊列)
 */
public void levelOrder(Visitor<E> visitor){
	if(root == null || visitor.stop) return;
	Queue<Node<E>> queue = new LinkedList<>(); // 隊列
	queue.offer(root);
	
	while(!queue.isEmpty()){
		Node<E> node = queue.poll();
		if(visitor.visit(node.element)) return;
		
		if(node.left != null) {
			queue.offer(node.left);
		}
		if(node.right != null) {
			queue.offer(node.right);
		}
	}
}

求二叉樹的高度: height()

遞歸實現

/**
 * 求樹的高度(遞歸)
 */
public int height() {
    return height(root);
}
public int height(Node<E> node) {
    if (node == null) return 0;
    return 1 + Math.max(height(node.left), height(node.right));
}

迭代實現

/**
 * 求樹的高度高度(迭代)
 */
public int height() {
    if (root == null) return 0;
    // 存儲每一層的元素數量, root!=null, 則首層必然有1個元素
    int levelSize = 1;
    int height = 0; // 樹的高度
    Queue<Node<E>> queue = new LinkedList<>();
    queue.offer(root);

    while (!queue.isEmpty()) {
        Node<E> node = queue.poll();
        levelSize--;
        if (node.left != null) {
            queue.offer(node.left);
        }
        if (node.right != null) {
            queue.offer(node.right);
        }
        if (levelSize == 0) { // 即將要訪問下一層
            levelSize = queue.size(); // 下一層的元素數量
            height++;
        }
    }
    return height;
}

是否爲完全二叉樹: isComplaete()

/**
 * 是否是完全二叉樹
 */
public boolean isComplete() {
    if (root == null) return false;

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

    // leaf代表是否要求後面都是葉子節點
    // 比如遍歷到一個節點 left == null && right == null
    //  或者是 left != null && right == null
    // 則要求這個節點後面的節點都是葉子節點
    boolean leaf = false;
    while (!queue.isEmpty()) {
        Node<E> node = queue.poll();
        // 要求是葉子結點,但是當前節點不是葉子結點
        if (leaf && !node.isLeaf()) {
            return false;
        }
        if (node.left != null) {
            queue.offer(node.left);
        } else if (node.right != null) {
            // node.left == null && node.right != null
            return false;
        }
        if (node.right != null) {
            queue.offer(node.right);
        } else {
            // node.left == null && node.right == null
            // node.left != null && node.right == null
            leaf = true; // 要求後面都是葉子節點
        }
    }
    return true;
}

求二叉樹的節點

前驅節點: predecessor()

在這裏插入圖片描述

/**
 * 前驅節點: 中序遍歷時的前一個節點
 * 求前驅節點
 */
protected Node<E> predecessor(Node<E> node) {
    if (node == null) return null;

    // 前驅節點在左子樹中(left.right.right.right....)
    Node<E> p = node.left;
    if (p != null) {
        // 左子樹不爲空,則找到它的最右節點
        while (p.right != null) {
            p = p.right;
        }
        return p;
    }

    // 能來到這裏說明左子樹爲空, 則從父節點、祖父節點中尋找前驅節點
    // 當父節點不爲空, 且某節點爲父節點的左子節點
    // 則順着父節點找, 直到找到【某結點爲父節點或祖父節點的右子樹中】時
    while (node.parent != null && node.parent.left == node) {
        node = node.parent;
    }

    // 來到這裏有以下兩種情況:
    // node.parent == null	無前驅, 說明是根結點
    // node.parent...right == node 找到【某結點爲父節點或祖父節點的右子樹中】
    // 那麼父節點就是某節點的前驅節點
    return node.parent;
}

後繼節點: successor()

在這裏插入圖片描述

/**
 * 後繼節點: 中序遍歷時的後一個節點
 * 求後繼節點
 */
protected Node<E> successor(Node<E> node) {
    if (node == null) return null;
    // 後繼節點與前驅節點正好相反

    // 後繼節點在右子樹中(node.right.left.left...)
    if (node.right != null) {
        Node<E> p = node.right;
        while (p.left != null) {
            p = p.left;
        }
        return p;
    }

    // 來到這裏說明沒有右節點, 則從父節點、祖父節點中尋找後繼節點
    // 當父節點不爲空, 且某節點爲父節點的右子節點
    // 則順着父節點找, 直到找到【某結點在父節點或祖父節點的左子樹中】時
    while (node.parent != null && node.parent.right == node) {
        node = node.parent;
    }

    // 來到這裏有以下兩種情況:
    // node.parent == null 無前驅,說明是根結點
    // node.parent.left == node 找到【某結點在父節點或祖父節點的左子樹中】
    // 那麼父節點就是某節點的後繼節點
    return node.parent;
}

BinaryTreeInfo 工具

這是 MJ 老師自己寫的一款工具,可以方便的打印二叉樹,git 地址如下:https://github.com/CoderMJLee/BinaryTrees

/**
 * BinaryTreeInfo 工具,用來打印二叉樹
 */
@Override
public Object root() {
	return root;
}
@Override
public Object left(Object node) {
	return ((Node<E>)node).left;
}
@Override
public Object right(Object node) {
	return ((Node<E>)node).right;
}
@Override
public Object string(Object node) {
	Node<E> myNode = (Node<E>)node;
	String parentStr = "null";
	if(myNode.parent != null){
		parentStr = myNode.parent.element.toString();
	}
	return myNode.element + "_p(" + parentStr + ")";
}

二叉樹完整源碼

package com.mj.tree;

import java.util.LinkedList;
import java.util.Queue;
import com.mj.printer.BinaryTreeInfo;

/**
 * 二叉樹(通用)
 */
@SuppressWarnings("unchecked")
// 實現BinaryTreeInfo接口是爲了使用打印二叉樹的工具,非必須
public class BinaryTree<E> implements BinaryTreeInfo {
    protected int size; // 元素數量
    protected Node<E> root; // 根節點

    /**
     * 訪問器接口 ——> 訪問器抽象類
     * 增強遍歷接口
     */
	/*public static interface Visitor<E>{
		void visit(E element);
	}*/
    public static abstract class Visitor<E> {
        boolean stop;
        // 如果返回true,就代表停止遍歷
        public abstract boolean visit(E element);
    }

    /**
     * 內部類,節點類
     */
    public static class Node<E> {
        E element;      // 元素值
        Node<E> left;   // 左節點
        Node<E> right;  // 右節點
        Node<E> parent; // 父節點

        public Node(E element, Node<E> parent) {
            this.element = element;
            this.parent = parent;
        }

        public boolean isLeaf() { // 是否葉子節點
            return left == null && right == null;
        }

        public boolean hasTwoChildren() { // 是否有兩個子節點
            return left != null && right != null;
        }
    }

    /**
     * 元素的數量
     */
    public int size() {
        return size;
    }

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

    /**
     * 清空所有的元素
     */
    public void clear() {
        root = null;
        size = 0;
    }

    /**
     * 前序遍歷
     */
    public void preorder(Visitor<E> visitor) {
        if (visitor == null) return;
        preorder(root, visitor);
    }
    public void preorder(Node<E> node, Visitor<E> visitor) {
        if (node == null || visitor.stop) return;
        // 根
        visitor.stop = visitor.visit(node.element);
        // 左
        preorder(node.left, visitor);
        // 右
        preorder(node.right, visitor);
    }

    /**
     * 中序遍歷
     */
    public void inorder(Visitor<E> visitor) {
        if (visitor == null) return;
        inorder(root, visitor);
    }
    public void inorder(Node<E> node, Visitor<E> visitor) {
        if (node == null || visitor.stop) return;
        // 左
        inorder(node.left, visitor);
        // 根
        if (visitor.stop) return;
        visitor.stop = visitor.visit(node.element);
        // 右
        inorder(node.right, visitor);
    }

    /**
     * 後序遍歷
     */
    public void postorder(Visitor<E> visitor) {
        if (visitor == null) return;
        postorder(root, visitor);
    }
    public void postorder(Node<E> node, Visitor<E> visitor) {
        if (node == null || visitor.stop) return;
        // 左
        postorder(node.left, visitor);
        // 右
        postorder(node.right, visitor);
        // 根
        if (visitor.stop) return;
        visitor.stop = visitor.visit(node.element);
    }

    /**
     * 層次遍歷
     */
    public void levelOrder(Visitor<E> visitor) {
        if (root == null || visitor.stop) return;
        Queue<Node<E>> queue = new LinkedList<>(); // 隊列
        queue.offer(root);

        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            if (visitor.visit(node.element)) return;

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

    /**
     * 求樹的高度(遞歸)
     */
    public int height1() {
        return height1(root);
    }
    public int height1(Node<E> node) {
        if (node == null) return 0;
        return 1 + Math.max(height1(node.left), height1(node.right));
    }

    /**
     * 求樹的高度高度(迭代)
     */
    public int height() {
        if (root == null) return 0;
        int levelSize = 1; // 存儲每一層的元素數量
        int height = 0; // 樹的高度
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            levelSize--;
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
            if (levelSize == 0) { // 即將要訪問下一層
                levelSize = queue.size();
                height++;
            }
        }
        return height;
    }

    /**
     * 是否是完全二叉樹
     */
    public boolean isComplete() {
        if (root == null) return false;

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

        // leaf代表是否要求後面都是葉子節點
        // 比如遍歷到一個節點 left == null && right == null
        //  或者是 left != null && right == null
        // 則要求這個節點後面的節點都是葉子節點
        boolean leaf = false;
        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            // 要求是葉子結點,但是當前節點不是葉子結點
            if (leaf && !node.isLeaf()) {
                return false;
            }
            if (node.left != null) {
                queue.offer(node.left);
            } else if (node.right != null) {
                // node.left == null && node.right != null
                return false;
            }
            if (node.right != null) {
                queue.offer(node.right);
            } else {
                // node.left == null && node.right == null
                // node.left != null && node.right == null
                leaf = true; // 要求後面都是葉子節點
            }
        }
        return true;
    }

    /**
     * 前驅節點: 中序遍歷時的前一個節點
     * 求前驅節點
     */
    protected Node<E> predecessor(Node<E> node) {
        if (node == null) return null;

        // 前驅節點在左子樹中(left.right.right.right....)
        Node<E> p = node.left;
        if (p != null) {
            // 左子樹不爲空,則找到它的最右節點
            while (p.right != null) {
                p = p.right;
            }
            return p;
        }

        // 能來到這裏說明左子樹爲空, 則從父節點、祖父節點中尋找前驅節點
        // 當父節點不爲空, 且某節點爲父節點的左子節點
        // 則順着父節點找, 直到找到【某結點爲父節點或祖父節點的右子樹中】時
        while (node.parent != null && node.parent.left == node) {
            node = node.parent;
        }

        // 來到這裏有以下兩種情況:
        // node.parent == null 無前驅, 說明是根結點
        // node.parent...right == node 找到【某結點爲父節點或祖父節點的右子樹中】
        // 那麼父節點就是某節點的前驅節點
        return node.parent;
    }

    /**
     * 後繼節點: 中序遍歷時的後一個節點
     * 求後繼節點
     */
    protected Node<E> successor(Node<E> node) {
        if (node == null) return null;
        // 後繼節點與前驅節點正好相反

        // 後繼節點在右子樹中(node.right.left.left...)
        if (node.right != null) {
            Node<E> p = node.right;
            while (p.left != null) {
                p = p.left;
            }
            return p;
        }

        // 來到這裏說明沒有右節點, 則從父節點、祖父節點中尋找後繼節點
        // 當父節點不爲空, 且某節點爲父節點的右子節點
        // 則順着父節點找, 直到找到【某結點在父節點或祖父節點的左子樹中】時
        while (node.parent != null && node.parent.right == node) {
            node = node.parent;
        }

        // 來到這裏有以下兩種情況:
        // node.parent == null 無前驅,說明是根結點
        // node.parent.left == node 找到【某結點在父節點或祖父節點的左子樹中】
        // 那麼父節點就是某節點的後繼節點
        return node.parent;
    }

    /**
     * BinaryTreeInfo 工具,用來打印二叉樹
     */
    @Override
    public Object root() {
        return root;
    }

    @Override
    public Object left(Object node) {
        return ((Node<E>) node).left;
    }

    @Override
    public Object right(Object node) {
        return ((Node<E>) node).right;
    }

    @Override
    public Object string(Object node) {
        Node<E> myNode = (Node<E>) node;
        String parentStr = "null";
        if (myNode.parent != null) {
            parentStr = myNode.parent.element.toString();
        }
        return myNode.element + "_p(" + parentStr + ")";
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章