After reading this article you will learn:
-
- What is a two binary tree
- Two special two-fork trees
- Full two fork Tree
- Complete binary Tree
- A contrast chart of two-and full-binary trees
- Realization of Binary Tree
- Using recursive node to realize the method of the left and right chain representation of a binary tree node
- Representation of a node with array subscript notation
- The main method of binary tree
- The creation of binary tree
- Adding elements to a binary tree
- Delete element of binary tree
- The emptying of the binary tree
- Get the height of a two-fork tree
- Number of nodes to get a two-fork tree
- Get the parent node of a node
- Traversal of a binary tree
- First Order traversal
- Middle Sequence traversal
- Post-post traversal
- Traverse Summary
- Summarize
There are many kinds of tree classification, but the basic is two fork tree derivation, today to learn the next two fork tree.
What is a two binary tree
Let's start with a definition:
A binary tree is a set of finite nodes, which can be an empty set or a set of root nodes and up to two sub-binary trees , one of which is called the left subtree of the root and the other is called the right subtree of the root.
To put it simply, a binary tree is a tree of up to two subtrees per node , as shown in:
The definition of a binary tree is a recursive definition, which is notable for the concept of the left and right sub-trees, since there are two trees below the same two-fork tree:
Two special two-fork trees
There are two special types of two-fork trees:
- Full two fork Tree
- Complete binary Tree
Full two fork Tree
In the above tree and Java implementations we introduce the definition of the height of the tree, and here the definition of a two-fork tree is:
If the height of a tree is K and has a 2^k-1 node, it is called a full two-fork tree.
What do you mean?
That is, each node must have either two subtrees trees or no subtrees.
Complete binary Tree
A complete binary tree is a special two-fork tree that meets the following requirements:
- All leaf nodes appear in K or k-1 layer, and the maximum number of nodes must be reached from 1 to k-1 layer.
- The K-level is not slow, but all nodes of the K-layer must be centered on the leftmost side.
To put it simply,
Is that the leaf node must be on the last or the penultimate level, and must be on the left. No node can be Zuozi without a right subtree.
A contrast chart of two-and full-binary trees
Take a picture and compare the two:
Realization of Binary Tree
A binary tree is simpler to implement than a normal tree because it has a maximum of two nodes.
A binary tree node is represented by recursive node implementation method/left-right chain representation
public class BinaryTreeNode { /* * 一个二叉树包括 数据、左右孩子 三部分 */ private int mData; private BinaryTreeNode mLeftChild; private BinaryTreeNode mRightChild; public BinaryTreeNode(int data, BinaryTreeNode leftChild, BinaryTreeNode rightChild) { mData = data; mLeftChild = leftChild; mRightChild = rightChild; } public int getData() { return mData; } public void setData(int data) { mData = data; } public BinaryTreeNode getLeftChild() { return mLeftChild; } public void setLeftChild(BinaryTreeNode leftChild) { mLeftChild = leftChild; } public BinaryTreeNode getRightChild() { return mRightChild; } public void setRightChild(BinaryTreeNode rightChild) { mRightChild = rightChild; }}
The tree represented by this implementation is created by the node, as shown in the image on the right:
Representation of a node with array subscript notation
public class BinaryTreeArrayNode { /** * 数组实现,保存的不是 左右子树的引用,而是数组下标 */ private int mData; private int mLeftChild; private int mRightChild; public int getData() { return mData; } public void setData(int data) { mData = data; } public int getLeftChild() { return mLeftChild; } public void setLeftChild(int leftChild) { mLeftChild = leftChild; } public int getRightChild() { return mRightChild; } public void setRightChild(int rightChild) { mRightChild = rightChild; }}
It is common to construct a two-fork tree using the nodes represented by the left and right chains.
The main method of binary tree
After having the node, we start to construct a binary tree, the main methods of the binary tree are:
- Create
- adding elements
- Delete Element
- Empty
- Traverse
- Get the height of the tree
- Gets the number of nodes in the tree
- Returns the parent node of a node
- ...
1. Creation of two fork trees
Creating a binary tree is as simple as having a binary root node and providing a way to set the root node:
public class BinaryTree { private BinaryTreeNode mRoot; //根节点 public BinaryTree() { } public BinaryTree(BinaryTreeNode root) { mRoot = root; } public BinaryTreeNode getRoot() { return mRoot; } public void setRoot(BinaryTreeNode root) { mRoot = root; }}
2. Adding elements of a two-fork tree
Since the binary tree has a sub-tree of left and right, it is also divided into two cases when adding elements: Add as Zuozi, or just a subtree:
public void insertAsLeftChild(BinaryTreeNode child){ checkTreeEmpty(); mRoot.setLeftChild(child); } public void insertAsRightChild(BinaryTreeNode child){ checkTreeEmpty(); mRoot.setRightChild(child); } private void checkTreeEmpty() { if (mRoot == null){ throw new IllegalStateException("Can‘t insert to a null tree! Did you forget set value for root?"); } }
Check that the root node is empty before each insert, and if it is, throw an exception (with Android source).
3. Two delete element of the fork tree
Deleting an element is simple, just set yourself to null.
But in order to avoid wasting useless memory, convenient GC recovery, we also need to traverse the left and right sub-tree of this element, set to null:
public void deleteNode(BinaryTreeNode node){ checkTreeEmpty(); if (node == null){ //递归出口 return; } deleteNode(node.getLeftChild()); deleteNode(node.getRightChild()); node = null;}
4. Two The emptying of the fork tree
The emptying of the binary tree is actually a special delete element – Delete the root node, so it's simple:
public void clear(){ if (mRoot != null){ deleteNode(mRoot); }}
5. Get the height of the two fork tree
In a binary tree, the height of the tree is the maximum value of each node degree.
So getting the height of the tree requires recursion to get the height of all the nodes and then the maximum value.
/** * 获取树的高度 ,特殊的获得节点高度 * @return */ public int getTreeHeight(){ return getHeight(mRoot); } /** * 获得指定节点的度 * @param node * @return */ public int getHeight(BinaryTreeNode node){ if (node == null){ //递归出口 return 0; } int leftChildHeight = getHeight(node.getLeftChild()); int rightChildHeight = getHeight(node.getRightChild()); int max = Math.max(leftChildHeight, rightChildHeight); return max + 1; //加上自己本身 }
6. The number of nodes to get a two-fork tree
To get the number of nodes for a two-fork tree, you need to traverse all the subtrees and add the sum.
public int getSize(){ return getChildSize(mRoot);}/** * 获得指定节点的子节点个数 * @param node * @return */public int getChildSize(BinaryTreeNode node){ if (node == null){ return 0; } int leftChildSize = getChildSize(node.getLeftChild()); int rightChildSize = getChildSize(node.getRightChild()); return leftChildSize + rightChildSize + 1;}
7. Get the parent node of a node
Since we use the nodes represented by the left and right subtrees and do not contain a parent node reference, sometimes it may also be necessary to return a method to the parent node of the specified node in the binary tree.
You need to traverse each subtree from the top down, and if the child of the root node of the subtree is the target node, return the node, or recursively traverse its right and left subtree:
/** * 获得指定节点的父亲节点 * @param node * @return */public BinaryTreeNode getParent(BinaryTreeNode node) { if (mRoot == null || mRoot == node) { //如果是空树,或者这个节点就是根节点,返回空 return null; } else { return getParent(mRoot, node); //否则递归查找 父亲节点 }}/** * 递归对比 节点的孩子节点 与 指定节点 是否一致 * * @param subTree 子二叉树根节点 * @param node 指定节点 * @return */public BinaryTreeNode getParent(BinaryTreeNode subTree, BinaryTreeNode node) { if (subTree == null) { //如果子树为空,则没有父亲节点,递归出口 1 return null; } //正好这个根节点的左右孩子之一与目标节点一致 if (subTree.getLeftChild() == node || subTree.getRightChild() == node) { //递归出口 2 return subTree; } //需要遍历这个节点的左右子树 BinaryTreeNode parent; if ((parent = getParent(subTree.getLeftChild(), node)) != null) { //左子树节点就是指定节点,返回 return parent; } else { return getParent(subTree.getRightChild(), node); //从右子树找找看 }}
Traversal of a binary tree
The traversal of the binary tree is introduced separately, because it is too important! I used to test this in my old exams.
As you can see from the previous operations, the recursive data structure of the binary tree makes it possible to use recursion for many operations.
The traversal of a binary tree is, in fact, a recursive traversal process that makes each node accessible and accessible only once.
The traversal of the binary tree is divided into three types according to the sequence of the root node, the left and right subtree traversal in different scenarios:
- First Order traversal
- Middle Sequence traversal
- Post-post traversal
Here, the first order, the middle order, the sequence refers to the root node relative to the left and right sub-tree traversal order.
First Order traversal
That is, the root node is traversed before the left and right sub-trees:
- Access the root node first
- Re-order Traversal Zuozi
- The first step is to traverse the right sub-tree
- Exit
Code:
/** * 先序遍历 * @param node */public void iterateFirstOrder(BinaryTreeNode node){ if (node == null){ return; } operate(node); iterateFirstOrder(node.getLeftChild()); iterateFirstOrder(node.getRightChild());}/** * 模拟操作 * @param node */public void operate(BinaryTreeNode node){ if (node == null){ return; } System.out.println(node.getData());}
Middle Sequence traversal
Traversal order:
- First middle order traversal Zuozi
- Re-access the root node
- Then the middle sequence traverses the right sub-tree
- Exit
Code:
/** * 中序遍历 * @param node */public void iterateMediumOrder(BinaryTreeNode node){ if (node == null){ return; } iterateMediumOrder(node.getLeftChild()); operate(node); iterateMediumOrder(node.getRightChild());}
Post-post traversal
That is, the root node is traversed after the left and right subtrees:
- Sequencing Traversal Zuozi
- And then go through the right sub-tree.
- Last Access root node
- Exit
Code:
/** * 后序遍历 * @param node */public void iterateLastOrder(BinaryTreeNode node){ if (node == null){ return; } iterateLastOrder(node.getLeftChild()); iterateLastOrder(node.getRightChild()); operate(node);}
Traverse Summary
As you can see, the difference between the three ways of traversal is the succession of recursion.
For example, there are three kinds of traversal results:
First-order Traversal:
1 2 4 5 7 3 6
Middle Sequence Traversal:
4 2 7 5 1 3 6
Post-post traversal:
4 7 5 2 6 3 1
Summarize
This article introduces the basic concepts of two-fork tree in data structure, common operations and three kinds of traversal methods.
There are three types of traversal methods that you might look at during an interview, and give you two kinds of traversal results that let you draw the actual two-tree structure. As long as you master the difference of three kinds of traversal methods, you can answer.
Revisit data structure: common methods of two-fork tree and three kinds of traversal methods Java implementation