So we’ve been talking about linear table structures, stacks, queues, and so on. Today we are going to talk about a nonlinear table structure called a tree. The Tree data structure is much more complex than the linear table data structure, the content is also more, first of all, let’s start with the Tree. @[toc]

A Tree (Tree)

Tree structure is a kind of nonlinear structure, which is characterized by branching and stratification among data elements.

1. Tree definition

A Tree is a finite set T composed of N (n≥0) nodes. When n=0, T is called an empty Tree. Otherwise, in any non-empty tree T: (1) there is only one specific node, which has no precursor node and is called Root node; (2) The remaining nodes can be divided into m(m≥0) disjoint subsets T1, T2… Tm, where each subset is itself a tree and is called a Subtree of the root.

Note: The definition of a tree is recursive, that is, there are trees within trees. The recursive definition of trees reveals the inherent properties of trees

2. What is tree structure

What is a tree? No good definition is as straightforward as a diagram. So I drew some “trees” in the picture. Let’s see, what are the features of these trees?

3. Why a tree structure

In an ordered array, you can quickly find a specific value, but to insert a new item into the ordered array, you must first find where the new item was inserted, and then move the larger item one bit back to make room for the new item. Similarly, deletion takes time. Obviously, if you’re going to do a lot of inserts and deletes and deletes, you shouldn’t use ordered arrays. On the other hand, you can quickly add and remove items from a linked list, but it’s not easy to find items in a linked list, and you have to access every item in the list from scratch until you find that item, which is a slow process. Trees are data structures that can be inserted and deleted as quickly as linked lists and searched as quickly as ordered arrays

4. Common terms for trees

Node – contains a data element and its tree branching degree, several points to subtree number of tree nodes have degrees – the biggest degree node in the tree Leaf nodes branch – degree zero (the terminal nodes) – node degree is not zero Children and parents – node subtree is called the root of the node’s children, in turn, This node is called the child’s parent siblings — the children’s ancestors and descendants of the same parent — from the root to the branch through which the node passes. Accordingly, any node in a subtree rooted at a node is called a descendant of that node. Hierarchy of nodes – The hierarchy of nodes is defined from the root, which has hierarchy 1 and its children at hierarchy 2… Cousins — the depth of the tree of nodes whose parents are in the same layer — the largest hierarchical ordered tree and unordered tree of nodes in the tree — if the subtrees of each node in the tree are considered to be ordered from left to right (that is, their positions are not interchangeable), the tree is called an ordered tree; Otherwise it is called an unordered tree. Forest — a finite set of m(m≥0) disjoint trees

Binary Tree

There are many kinds of tree structures, but the most commonly used tree is binary tree, the most commonly used tree is binary tree. Each node of a binary tree has a maximum of two children, namely, the left child and the right child. In binary trees, there are two special trees, namely full binary tree and complete binary tree. Full binary tree is a special case of complete binary tree.

1. The definition and characteristics of binary tree

A Binary Tree is a finite set BT of N (n≥0) nodes, which is either an empty set or consists of a root node and two disjoint Binary trees called left and right subtrees respectively. The characteristics of ———————————— binary tree: each node has at most two subtrees (that is, no node with a degree greater than 2); The subtrees of a binary tree can be divided into left and right, and their order cannot be reversed arbitrarily.

2. Some special forms of binary trees

1. Full binary tree: Definition: a full binary tree with a depth of K and 2K-1 nodes. 2. Complete binary tree: Definition: a binary tree with depth K and n nodes is called a complete binary tree if and only if each node corresponds to the nodes numbered from 1 to N in the full binary tree with depth K. Leaf nodes can only occur on the two largest layers of the hierarchy; Feature 2: For any node, if the maximum level of descendants under its right branch is L, the maximum level of descendants under its left branch must be L or L +1

It is suggested to read the picture and understand the text comprehensively

Code creates a binary tree

First, create a Node Node class

package demo5;
/* * node (node) point class */
public class Node {
	// The weight of the node
	int value;
	// Left son (left node)
	Node leftNode;
	// Right son (right node)
	Node rightNode;
	// constructor, which assigns weights to the binary tree when initialized
	public Node(int value) {
		this.value=value;
	}
	
	// Set the left son (left node)
	public void setLeftNode(Node leftNode) {
		this.leftNode = leftNode;
	}
	// Set the right son (right node)
	public void setRightNode(Node rightNode) {
		this.rightNode = rightNode;
	}
Copy the code

Next, create a BinaryTree class, BinaryTree

package demo5;
/* * binary tree Class */
public class BinaryTree {
    // Root node root
	Node root;
	
	// Set the root node
	public void setRoot(Node root) {
		this.root = root;
	}
	
	// Get the root node
	public Node getRoot(a) {
		returnroot; }}Copy the code

Finally, create the TestBinaryTree class (which is mainly used to test the main method) to create a binary tree

package demo5;
public class TestBinaryTree {

	public static void main(String[] args) {
		// Create a tree
		BinaryTree binTree = new BinaryTree();
		// Create a root node
		Node root = new Node(1);
		// Assign the root node to the tree
		binTree.setRoot(root);
		// Create a left node
		Node rootL = new Node(2);
		// Set the newly created node as a child of the root node
		root.setLeftNode(rootL);
		// Create a right node
		Node rootR = new Node(3);
		// Set the newly created node as a child of the root node
		root.setRightNode(rootR);
		// Create two child nodes for the layer 2 left node
		rootL.setLeftNode(new Node(4));
		rootL.setRightNode(new Node(5));
		// Create two child nodes for the right node of the second layer
		rootR.setLeftNode(new Node(6));
		rootR.setRightNode(new Node(7)); }}Copy the code

The following sections of traversal, node finding, and node deletion will all revolve around these three classes

It is not difficult to see that the created binary tree is as follows:

3. Two ways to store binary trees

Binary trees can be stored either chained or array sequentially. Array sequential storage is more suitable for complete binary trees. Other types of binary trees will waste storage space by array storage, so chain storage is more suitable.

Let’s look at the simple, intuitive chain storage method.

Sequential storage of arrays

4. Traversal of binary trees

I’ve talked about the basic definition of binary trees and how to store them. Now let’s look at the very important operation in binary trees, the traversal of binary trees. This is also a very common interview question.

There are three methods of classical traversal: pre-order traversal, mid-order traversal and post-order traversal

Pre-traversal means that, for any node in the tree, the node is printed first, then its left subtree, and finally its right subtree.

Middle-order traversal means that, for any node in the tree, the left subtree is printed first, then itself, and finally the right subtree.

Post-traversal means that, for any node in the tree, the left subtree is printed first, then the right subtree, and finally the node itself.

I’m sure you’re smart enough to realize that a binary tree traversal is a recursive process

On top of the binary tree code we created earlier, let’s iterate through ~ using these three methods

The same method is added to the Node class: you can see that the traversal methods are all recursive

package demo5;
/* * node (node) point class */
public class Node {
/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = began to traverse the = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
	// preorder traversal
	public void frontShow(a) {
		// Iterate over the contents of the current node
		System.out.println(value);
		/ / the left node
		if(leftNode! =null) {
			leftNode.frontShow();
		}
		/ / right node
		if(rightNode! =null) { rightNode.frontShow(); }}// middle order traversal
	public void midShow(a) {
		// Left child node
		if(leftNode! =null) {
			leftNode.midShow();
		}
		// The current node
		System.out.println(value);
		// Right child node
		if(rightNode! =null) { rightNode.midShow(); }}// after the sequence traversal
	public void afterShow(a) {
		// Left child node
		if(leftNode! =null) {
			leftNode.afterShow();
		}
		// Right child node
		if(rightNode! =null) {
			rightNode.afterShow();
		}
		// The current nodeSystem.out.println(value); }}Copy the code

The method is then added to the BinaryTree class again, and the added method calls the traversal method in the Node class

package demo5;
/* * binary tree Class */
public class BinaryTree {

	public void frontShow(a) {
		if(root! =null) {
			// Call the forward-traversal frontShow () method of the Node class Noderoot.frontShow(); }}public void midShow(a) {
		if(root! =null) {
			// Call the midShow() method of the Node class Noderoot.midShow(); }}public void afterShow(a) {
		if(root! =null) {
			// Call the afterShow() method of the Node class Noderoot.afterShow(); }}}Copy the code

We’re still testing in the TestBinaryTree class

package demo5;

public class TestBinaryTree {

	public static void main(String[] args) {
		// Walk the tree forward
		binTree.frontShow();
		System.out.println("= = = = = = = = = = = = = = =");
		// middle order traversal
		binTree.midShow();
		System.out.println("= = = = = = = = = = = = = = =");
		// after the sequence traversal
		binTree.afterShow();
		System.out.println("= = = = = = = = = = = = = = =");
		// preorder search
		Node result = binTree.frontSearch(5);
		System.out.println(result);
		
}
Copy the code

If recursion is not well understood, I can share a small way to learn: I suggest that you can do this breakpoint debugging, step by step, thinking to keep up, carefully examine each step of the operation believe me, you will re-understand recursion! (Paste a picture like the following step by step break point thinking more clearly)

The recursive implementation
Low efficiency
Nonrecursive ergodic algorithm

Analysis of binary tree traversal calculation method:

The basic operation of binary tree traversal algorithm is to visit the root node. No matter which order is traversal, all nodes are accessed. The time complexity of binary tree containing N nodes is O (n). The required auxiliary space is the stack space required in the traversal process, which is at most equal to the depth of the binary tree K times the number of space required by each node. In the worst case, the depth of the tree is the number of nodes N. Therefore, its space complexity is also O(n).

5. Search and delete nodes in binary tree

Just talked about three kinds of binary tree traversal put method, then the search of nodes can also be followed, respectively called pre-order search, middle-order search and post-order search, the following code only before order search as an example, the three search method ideas similar ~

As for deleting nodes, there are three cases:

If the root node is deleted, then the binary tree is completely deleted. If the parent node is deleted, then the subtree formed by the parent node and all its children is deleted. If the leaf node is deleted, then the binary tree is deleted directly

So, I post the complete three classes (create, traverse, find, delete)

It’s still the Node class

package demo5;
/* * node (node) point class */
public class Node {
	// The weight of the node
	int value;
	/ / left son
	Node leftNode;
	/ / right son
	Node rightNode;
	// constructor, which assigns weights to the binary tree when initialized
	public Node(int value) {
		this.value=value;
	}
	
	// Set the left son
	public void setLeftNode(Node leftNode) {
		this.leftNode = leftNode;
	}
	// Set the right son
	public void setRightNode(Node rightNode) {
		this.rightNode = rightNode;
	}
	
	// preorder traversal
	public void frontShow(a) {
		// Iterate over the contents of the current node
		System.out.println(value);
		/ / the left node
		if(leftNode! =null) {
			leftNode.frontShow();
		}
		/ / right node
		if(rightNode! =null) { rightNode.frontShow(); }}// middle order traversal
	public void midShow(a) {
		// Left child node
		if(leftNode! =null) {
			leftNode.midShow();
		}
		// The current node
		System.out.println(value);
		// Right child node
		if(rightNode! =null) { rightNode.midShow(); }}// after the sequence traversal
	public void afterShow(a) {
		// Left child node
		if(leftNode! =null) {
			leftNode.afterShow();
		}
		// Right child node
		if(rightNode! =null) {
			rightNode.afterShow();
		}
		// The current node
		System.out.println(value);
	}

	// preorder search
	public Node frontSearch(int i) {
		Node target=null;
		// Compare the value of the current node
		if(this.value==i) {
			return this;
		// The value of the current node is not the node to look for
		}else {
			// Find the left son
			if(leftNode! =null) {
				// Target is still null
				target = leftNode.frontSearch(i);
			}
			// If it is not empty, it is found in the left son
			if(target! =null) {
				return target;
			}
			// Find the right son
			if(rightNode! =null) { target=rightNode.frontSearch(i); }}return target;
	}
	
	// Delete a subtree
	public void delete(int i) {
		Node parent = this;
		// Judge the left son
		if(parent.leftNode! =null&&parent.leftNode.value==i) {
			parent.leftNode=null;
			return;
		}
		// Determine the right son
		if(parent.rightNode! =null&&parent.rightNode.value==i) {
			parent.rightNode=null;
			return;
		}
		
		// Recursively check and delete the left son
		parent=leftNode;
		if(parent! =null) {
			parent.delete(i);
		}
		
		// Recursively check and delete the right son
		parent=rightNode;
		if(parent! =null) { parent.delete(i); }}}Copy the code

It’s still a BinaryTree

package demo5;
/* * binary tree Class */
public class BinaryTree {
    // Root node root
	Node root;
	
	// Set the root node
	public void setRoot(Node root) {
		this.root = root;
	}
	
	// Get the root node
	public Node getRoot(a) {
		return root;
	}

	public void frontShow(a) {
		if(root! =null) {
			// Call the forward-traversal frontShow () method of the Node class Noderoot.frontShow(); }}public void midShow(a) {
		if(root! =null) {
			// Call the midShow() method of the Node class Noderoot.midShow(); }}public void afterShow(a) {
		if(root! =null) {
			// Call the afterShow() method of the Node class Noderoot.afterShow(); }}// find node I
	public Node frontSearch(int i) {
		return root.frontSearch(i);
	}
	// Delete node I
	public void delete(int i) {
		if(root.value==i) {
			root=null;
		}else{ root.delete(i); }}}Copy the code

It’s still the TestBinaryTree test class

package demo5;

public class TestBinaryTree {

	public static void main(String[] args) {
		// Create a tree
		BinaryTree binTree = new BinaryTree();
		// Create a root node
		Node root = new Node(1);
		// Assign the root node to the tree
		binTree.setRoot(root);
		// Create a left node
		Node rootL = new Node(2);
		// Set the newly created node as a child of the root node
		root.setLeftNode(rootL);
		// Create a right node
		Node rootR = new Node(3);
		// Set the newly created node as a child of the root node
		root.setRightNode(rootR);
		// Create two child nodes for the layer 2 left node
		rootL.setLeftNode(new Node(4));
		rootL.setRightNode(new Node(5));
		// Create two child nodes for the right node of the second layer
		rootR.setLeftNode(new Node(6));
		rootR.setRightNode(new Node(7));
		// Walk the tree forward
		binTree.frontShow();
		System.out.println("= = = = = = = = = = = = = = =");
		// middle order traversal
		binTree.midShow();
		System.out.println("= = = = = = = = = = = = = = =");
		// after the sequence traversal
		binTree.afterShow();
		System.out.println("= = = = = = = = = = = = = = =");
		// preorder search
		Node result = binTree.frontSearch(5);
		System.out.println(result);
		
		System.out.println("= = = = = = = = = = = = = = =");
		// Delete a subtree
		binTree.delete(4); binTree.frontShow(); }}Copy the code

So, just to summarize, we’ve learned about a nonlinear table data structure called a tree. There are some common concepts you need to know about trees: root, leaf, parent, child, and sibling nodes, as well as the height, depth, number of layers of nodes, and the height of the tree. The most common tree we use is a binary tree. Each node of a binary tree has a maximum of two children, namely, the left child and the right child. In binary trees, there are two special trees, namely full binary tree and complete binary tree. Full binary tree is a special case of complete binary tree. Binary trees can be stored either chained or array sequentially. Array sequential storage is suitable for complete binary trees, other types of binary trees using array storage will waste storage space. In addition to that, the very important operations in binary trees are the front, middle, and back traversal, traversal in order O(n) time, and you need to understand and implement it in recursive code.

If this article is helpful to you, even a little bit, please give a thumbs up. Thank you

Welcome to pay attention to my public number, discuss technology together, yearning for technology, the pursuit of technology… Said come is a friend oh…