【Data structure】_7. Binary tree

Table of contents

1. Tree structure

1.1 The concept of a tree

1.2 Related concepts of trees

1.3 Tree Representation

1.4 The application of the tree in practice—representing the directory tree structure of the file system

EDIT 2. Binary tree

2.1 Concept

2.2 Special binary tree

 2.3 Properties of Binary Trees

2.4 Storage structure of binary tree

2.4.1 Sequential storage structure (array storage structure)

2.4.2 Chain storage structure

2.5 Basic operation of binary tree

2.5.1 Depth-first traversal of a binary tree

2.5.2 Breadth-first traversal of binary tree

2.5.3 Node Statistics of Binary Tree

2.5.4 Leaf node statistics of binary tree

 2.5.5 K-th layer node statistics of binary tree

 2.5.6 Get the height of the binary tree

2.5.7 Detect whether an element whose value is value exists

2.5.8 Determine whether a tree is a complete binary tree

2.6 Non-recursive methods for binary trees

2.6.1 Non-recursive implementation of preorder traversal

2.6.2 Non-recursive implementation of inorder traversal

2.6.3 Non-recursive post-order traversal


1. Tree structure

1.1 The concept of a tree

A tree is a nonlinear data structure. It is a collection of n (n>0) finite nodes with a hierarchical relationship. It is called a tree because it looks like an upside-down tree, rooted on , the branches and leaves are below.

① The root node is a special node without a predecessor node;

② Except the root node, other nodes are divided into M (M>0) disjoint sets T1, T2, T3...Tm, each of which Ti (1<=i<=m) is a A subtree whose structure is similar to that of a tree. The root node of each tree has one and only one predecessor, and can have 0 or more successors;

③ Therefore, the tree is defined recursively;

PS: In the tree structure, there can be no intersection between subtrees, that is, except for the root node, each node has and only one direct predecessor;

1.2 Related concepts of trees

(1) Degree of node : the number of subtrees contained in a node, for example, the degree of node A is 6;

(2) Leaf nodes or terminal nodes : nodes with degree 0, such as H, I, P, Q, K, L, M, N;

(3) Non-terminal nodes or branch nodes: nodes whose degree is not 0, such as: D, E, F, G, J;

(4) Parent node or parent node : a node containing a child node is called the parent node of its child node, such as A is the parent node of B;

(5) Child node or child node : the root node of the subtree contained in a node is called the child node of the node, such as B is a child node of A;

(6) Brother nodes: nodes with the same parent node, such as B and C are sibling nodes;

(7) Degree of number: The degree of the largest node in a tree is called the degree of number, as shown in the above figure, the degree of the tree is 6;

(8) The level of nodes: defined from the root, the root is the first level, the child nodes of the root are the second level, and so on;

(9) The height or depth of the tree: the maximum level of nodes in the tree, such as the height or depth of the tree in the above figure is 4;

(10) Cousin nodes: nodes whose parents are on the same layer are called cousins, such as H and I are cousin nodes;

(11) Ancestor of a node: all nodes on the branch from the root to the node, such as A is the ancestor of all nodes;

(12) Descendants: Any node in the subtree rooted at a certain node becomes a descendant of the node, for example, all nodes are descendants of A;

(13) Forest: A collection of m (m>0) disjoint trees is called a forest;

1.3 Tree Representation

The storage of a tree is much more complicated than that of a linear table. It needs to store both the value range and the relationship between nodes;

There are many representations of trees, such as parent notation, child notation, and child-parent notation.

The most commonly used is: left-child-right-sibling notation.

Graphic:

 The code means:

class Node{
    int val;     // 数据域
    Node firstChild;   // 第一个孩子引用
    Node nextBrother;  // 下一个兄弟引用
}
//注意此处的兄弟指的是亲兄弟而非堂兄弟,即此处指向的兄弟有相同的祖先

1.4 The application of the tree in practice—representing the directory tree structure of the file system


2. Binary tree

2.1 Concept

(1) The node of a binary tree is a finite set, the set: ① or empty ② consists of a root node plus two binary trees called left subtree and right subtree;

 (2) Features:

    ① There is no node with degree greater than 2;

    ② The subtrees of the binary tree are divided into left and right, and the order cannot be reversed, so the binary tree is an ordered tree;

(3) Any kind of binary tree is compounded by ① empty tree ② only root node ③ only left subtree ④ only right subtree ⑤ both left and right subtrees exist;

2.2 Special binary tree

(1) Full binary tree:

 A binary tree with the maximum number of nodes in each layer , that is, a binary tree with k layers and a total number of nodes of 2^k-1 is a complete binary tree.

(2) Complete binary tree:

For a binary tree with a depth of k, the nodes in the first k-1 layers are full , and the last layer is not satisfied but satisfied, and the existing nodes are continuous from left to right .

 2.3 Properties of Binary Trees

(1) If the number of layers of the root node is specified as 1, then there are at most 2^(i-1) nodes on the i-th layer of a non-empty binary tree ;

(2) If the number of root node layers is specified as 1, then the maximum number of nodes in a binary tree with a depth of h is 2^k-1 ;

(3) For any binary tree, if the number of nodes with degree 0 is n0, and the number of branch nodes with degree 2 is n2, then n0=n2+1 ;

(4) If the number of layers of the root node is specified as 1, the depth of a full binary tree with n nodes is h=log2(n+1) (base 2);

Example 1 : A binary tree has a total of 399 nodes, of which 199 are nodes with a degree of 2, then the number of leaf nodes in the binary tree is (B)

A. There is no such binary tree B. 200 C. 198 D. 199   

Analysis: According to the third property, the number of leaf nodes is the number of nodes with a degree of 0, and the answer is obtained according to the third property.

Example 2 : In a complete binary tree with 2n nodes, the number of leaf nodes is (A)

A.n  B.n+1 C. n-1  D.n/2

Analysis: A node with a degree of 0 is marked as n0, a node with a degree of 1 is marked as n1, and a node with a degree of 2 is marked as n2. According to the meaning of the question:

n0+n1+n2=2n, combined with the third property: n2=n0-1, substituting 2*n0-1+n1=2n, for a complete binary tree

In other words, there can only be 0 or 1 node with degree 1. Here, if n1=0, then n0 is a decimal, so n1 can only be 1, so the node with degree 0

The number of points is n.

Example 3: The number of nodes in a complete binary tree is 531, then the height of this tree is (B)

A.11 B. 10  C.8  D.12

Analysis: For a complete binary tree with a height of h, the range of nodes is [2^(h-1), 2^h-1], and the answer is obtained by substituting options for upper and lower limit calculations.

Example 4: A complete binary tree with 767 nodes, the number of leaf nodes is (B)

A.383  B. 384  C.385  D.386

Brief analysis, the same idea as Example 2

2.4 Storage structure of binary tree

There are two storage methods for binary tree storage:

2.4.1 Sequential storage structure (array storage structure)

① Generally, the use of array storage is only suitable for representing a complete binary tree or a full binary tree. If it is a general binary tree, there will be a lot of space waste;

In actual use, only pairs will be stored in arrays;

② The sequential storage of a binary tree is physically an array and logically a binary tree.

③ At the same time, sequential storage can calculate the parent-child relationship of nodes according to the subscript:

Know the father and seek the son: leftchild=parent*2+1, leftchild=parent*2+2;

Tomoko's father: (parent-1)/2;

2.4.2 Chain storage structure

The linked storage of the binary tree is referenced by nodes one by one. The common representations are:

(1) Binary representation (child representation):

class Node{
    int val;   // 数据域
    Node left;   // 左孩子引用(常代表以左孩子为根的整个左子树)
    Node right;  // 右孩子引用(常代表以右孩子为根的整个右子树)
}

 (2) Trident representation (child-parent representation):

class Node{
    int val;   // 数据域
    Node left;   // 左孩子引用(常代表以左孩子为根的整个左子树)
    Node right;  // 右孩子引用(常代表以右孩子为根的整个右子树)
    Node parent; // 当前结点的根节点
}

 The child-parent representation is mainly used in balanced trees. In this paper, the child representation is used to construct a binary tree;

2.5 Basic operation of binary tree

2.5.1 Depth-first traversal of a binary tree

Depth-first traversal includes pre-order traversal, in-order traversal and post-order traversal;

Preorder traversal: the operation of accessing the root node occurs before traversing its left and right subtrees; (following -> left subtree -> right subtree)

In-order traversal: the operation of accessing the root node occurs in traversing its left and right subtrees; (left subtree -> root -> right subtree)

Post-order traversal: the operation of accessing the root node occurs after traversing its left and right subtrees; (left subtree -> right subtree -> root)

Use # to indicate empty:

Preorder traversal: 1 2 3 # # # 4 5 # # 6 # # 

Inorder traversal: # 3 # 2 # 1 # 5 # 4 # 6 # 

Post-order traversal: # # 3 # 2 # # 5 # # 6 4 1

Based on the following binary tree structure:

Its recursive implementation code is as follows:

    // 前序遍历:根  左子树  右子树  递归
    public void preOrder(TreeNode root){
        if(root == null){
            return;
        }
        System.out.print(root.val+"  ");
        preOrder(root.left);
        preOrder(root.right);
    }
    // 中序
    public void inOrder(TreeNode root){
        if(root == null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+"  ");
        inOrder(root.right);
    }
    // 后序
    public void postOrder(TreeNode root){
        if(root == null){
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val+"  ");
    }

After the object is created, it is called separately, and the output result is:

  

Note: In addition to the way of writing the empty return value, you can also make the depth-first traversal have a return value:

(1) The way of writing traversal ideas:

    List<Character> ret = new ArrayList<>();
    public List<Character> preorderTraversal(TreeNode root){
        if(root == null){
            return ret;
        }
        ret.add(root.val);
        preorderTraversal(root.left);
        preorderTraversal(root.right);
        return ret;
    }

(2) Ways of writing sub-questions:

    public List<Character> preorderTraversal(TreeNode root){
        List<Character> ret = new ArrayList<>();
        if(root == null){
            return ret;
        }
        //System.out.print(root.val+" ");
        ret.add(root.val);

        List<Character> leftTree = preorderTraversal(root.left);
        ret.addAll(leftTree);

        List<Character> rightTree = preorderTraversal(root.right);
        ret.addAll(rightTree);
        return ret;
    }

2.5.2 Breadth-first traversal of binary tree

Create a queue, let cur traverse from root, put root into the queue under the condition that the root node is not empty, and judge whether root.left and root.right are empty in turn, and enter the queue if they are not empty. When the queue is not empty When , pop the first element from the stack and set root = cur;

    public void levelOrder(TreeNode root){
        if(root == null){
            return;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode cur = queue.poll();
            System.out.print(cur.val+" ");
            if(cur.left != null){
                queue.offer(cur.left);
            }
            if(cur.right != null){
                queue.offer(cur.right);
            }
        }
    }

The output is:

  

2.5.3 Node Statistics of Binary Tree

(1) Idea 1: Sub-problem idea:

    public int size(TreeNode root){
        // 左树结点+右树结点+根节点
        // 写法1:
        //return root == null? 0: size(root.left)+size(root.right)+1;
        // 写法2:
        if(root == null){
            return 0;
        }
        int leftSize = size(root.left);
        int rightSize = size(root.right);
        return leftSize + rightSize + 1;
    }

(2) Idea 2: Traversing ideas:

    public int nodeSize;
    public void size2(TreeNode root){
        if(root == null){
            return;
        }
        nodeSize++;
        size2(root.left);
        size2(root.right);
    }

The test code is:

        System.out.println("子问题思路求二叉树结点:");
        System.out.println(binaryTree.size(binaryTree.root));
        System.out.println("遍历思路求二叉树结点:");
        binaryTree.size2(binaryTree.root);
        System.out.println(binaryTree.nodeSize);

The output is:

2.5.4 Leaf node statistics of binary tree

(1) Idea 1: Sub-problem idea:

    public int getLeafNodeCount(TreeNode root){
        if(root == null){
            return 0;
        }
        if(root.left==null && root.left == null){
            return 1;
        }
        int leftTreeNode = getLeafNodeCount(root.left);
        int rightTreeNode = getLeafNodeCount(root.right);
        return leftTreeNode+rightTreeNode;
    }

(2) Idea 2: Traversing ideas:

    public int leafNode = 0;
    public void getLeafNodeCount2(TreeNode root){
        if(root == null){
            return;
        }
        if(root.left == null && root.right == null){
            leafNode++;
        }
        getLeafNodeCount2(root.left);
        getLeafNodeCount2(root.right);
    }

The test code is:

        System.out.println("子问题思路求叶子节点的个数为:");
        System.out.println(binaryTree.getLeafNodeCount(binaryTree.root));
        System.out.println("遍历思路求叶子结点个数为:");
        binaryTree.getLeafNodeCount2(binaryTree.root);
        System.out.println(binaryTree.leafNode);

The output is:

 2.5.5 K-th layer node statistics of binary tree

(Only show sub-question ideas)

    public int getKLevelNodeCount(TreeNode root, int k){
        if(root == null){
            return 0;
        }
        if(k==1){
            return 1;
        }
        int leftTreeNode = getKLevelNodeCount(root.left,k-1);
        int rightTreeNode = getKLevelNodeCount(root.right,k-1);
        return leftTreeNode+rightTreeNode;
    }

The test code is:

        TestBinaryTree binaryTree = new TestBinaryTree();
        binaryTree.root = binaryTree.createTree();
        System.out.println("请输入要查询结点的层数:");
        Scanner scanner = new Scanner(System.in);
        int k = scanner.nextInt();
        System.out.println("第"+k+"层结点数为:");
        System.out.println(binaryTree.getKLevelNodeCount(binaryTree.root,k));

The output is:

 2.5.6 Get the height of the binary tree

    public int getHeight(TreeNode root){
        // 二叉树高度是左右子树高度的较大值+1
        if(root==null){
            return 0;
        }
        int leftTreeHeight = getHeight(root.left);
        int rightTreeHeight = getHeight(root.right);
        return (leftTreeHeight>rightTreeHeight)?leftTreeHeight+1:rightTreeHeight+1;
    }

The test code is:

        TestBinaryTree binaryTree = new TestBinaryTree();
        binaryTree.root = binaryTree.createTree();
        System.out.println("当前二叉树高度为:");
        System.out.println(binaryTree.getHeight(binaryTree.root));

The output is:

2.5.7 Detect whether an element whose value is value exists

    public TreeNode find(TreeNode root, char val){
        // 前序遍历二叉树
        if(root == null){
            return null;
        }
        if(root.val == val){
            return root;
        }
        TreeNode leftTree = find(root.left, val);
        if(leftTree != null){
            return leftTree;
        }
        TreeNode rightTree = find(root.right, val);
        if(rightTree != null){
            return rightTree;
        }
        return null;
    }

The test code is:

        TestBinaryTree binaryTree = new TestBinaryTree();
        binaryTree.root = binaryTree.createTree();
        System.out.println("请输入要查询的value值:");
        char value = (char)System.in.read();
        TestBinaryTree.TreeNode ret = binaryTree.find(binaryTree.root, value);
        if(ret != null){
            System.out.println(ret.val);
        }else {
            System.out.println(ret);
        }

The output is:
      

2.5.8 Determine whether a tree is a complete binary tree

Create a queue. On the premise that the root node is not empty, first put the root node into the queue, and then each time a head element pops up, put its left and right child nodes into the queue until the head element is empty, if so When the elements in the queue are all empty, it is a complete binary tree, otherwise it is not a complete binary tree;

code:

    public boolean isCompleteTree(TreeNode root){
        if(root == null){
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);     // 将根结点入队列
        while( !queue.isEmpty()) {
            // 每弹出一个队首元素,就将该元素的左右孩子入队列;
            TreeNode cur = queue.poll();
            if (cur != null) {
                queue.offer(cur.left);
                queue.offer(cur.right);
            } else {
                //  一直出队列队首元素直至队首元素为null
                break;
            }
        }
        
        while(!queue.isEmpty()){
            TreeNode tmp = queue.poll();
            if(tmp != null){
                return false;
            }
        }
        return true;
    }

The test code is:

        TestBinaryTree binaryTree = new TestBinaryTree();
        binaryTree.root = binaryTree.createTree();
        System.out.println("判断是否为完全二叉树:");
        System.out.println(binaryTree.isCompleteTree(binaryTree.root));
        System.out.println();

The output is:

 Based on the original binary tree, delete the right child H node of the E node, and the test results are as follows:

2.6 Non-recursive methods for binary trees

2.6.1 Non-recursive implementation of preorder traversal

Define the cur node to traverse the binary tree. Starting from the root node root, let cur traverse the left subtree in turn. On the premise that the left child of the node is not empty, push the nodes into the stack one by one. Each time a node is pushed into the stack, Just print a node, when encountering a node whose left child is empty, pop the top element of the stack and make cur its right child;

    public void preOrderNor(TreeNode root){
        if(root == null){
            return;
        }
        TreeNode cur = root;
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                System.out.print(cur.val + " ");
                cur = cur.left;
            }
            // 此时为cur为空但栈不为空:
            // 令cur为栈顶元素的右孩子即可
            TreeNode top = stack.pop();
            cur = top.right;
        }
    }

2.6.2 Non-recursive implementation of inorder traversal

    public void inOrderNor(TreeNode root){
        if(root == null){
            return;
        }
        TreeNode cur = root;
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            System.out.print(top.val+" ");
            cur = top.right;
        }
    }

2.6.3 Non-recursive post-order traversal

   public void postOrderNor(TreeNode root){
        if(root == null){
            return;
        }
        TreeNode cur = root;
        TreeNode prev = null;
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right == null || top.right == prev){
                // 栈顶元素没有右孩子,根据左->右->根,可以直接打印根结点的值
                System.out.print(top.val+" ");
                stack.pop();
                prev = top;
            }else {
                // 栈顶元素有右孩子,还需遍历当前栈顶元素结点的右子树
                cur = top.right;
            }
        }
    }

The test code for non-recursive implementation of pre-middle-post order traversal is as follows:

        TestBinaryTree binaryTree = new TestBinaryTree();
        binaryTree.root = binaryTree.createTree();
        System.out.println("非递归实现前序遍历:");
        binaryTree.preOrderNor(binaryTree.root);
        System.out.println();
        System.out.println("非递归实现中序遍历:");
        binaryTree.inOrderNor(binaryTree.root);
        System.out.println();
        System.out.println("非递归实现后序遍历:");
        binaryTree.postOrderNor(binaryTree.root);
        System.out.println();

 The output is as follows:

Guess you like

Origin blog.csdn.net/m0_63299495/article/details/132024798