Data structure and algorithm exercises (3) Binary tree


1. tree

树是一种非线性的数据结构,是由n(n >=0)个结点组成的有限集合
如果n=0,树为空树。
如果n>0,除根节点外,其余结点被分成m(m>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。

insert image description here
Related concepts:

  • Root node: A node that has no parent node.
  • Leaf node: A node that has no child nodes.
  • Sibling nodes: nodes with the same parent node;
  • Degree of node: the number of subtrees owned by the node. For example, the degree of node A is 3. B, C is 2
  • Degree of tree: The maximum degree of each node in the tree. For example, the degree of node A is the largest, so the degree of the tree is 3
  • The level of the node: starting from the definition of the root, the root is the 1st level, the child nodes of the root are the 2nd level, and so on;
  • The height or depth of the tree: the maximum level of nodes in the tree; as shown above: the height of the tree is 4
  • Forest: A collection of m (m>0) disjoint trees is called a forest.

2. Binary tree

二叉树(Binary tree)是每个节点最多有两个子节点的树。(删掉上面的D节点)

insert image description here

3. Full binary tree

 一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
 如果一个二叉树的层数为n,且结点总数是2^n -1 ,则它就是满二叉树

insert image description here

4. Complete binary tree

完全二叉树是一种特殊的二叉树,它除了最后一层外,其他每一层都被完全填满,且最后一层的节点都靠左排列。
满二叉树一定是完全二叉树

insert image description here

5. Binary tree traversal (preorder, midorder, postorder)

According to different rules (see the order of output parent nodes), it is divided into:

  • Preorder traversal: output the parent node first, then traverse the left subtree, and then the right subtree

  • In-order traversal: first traverse the left subtree, then output the parent node, and then traverse the right subtree

  • Post-order traversal: first traverse the left subtree, then traverse the right subtree, and finally output the parent node

    例如上面的完全二叉树:
        前序遍历:ABEFCG
        中序遍历:EBFAGC
        后序遍历:EFBGCA
    

Code:

package Tree;
public class BinaryTreeDemo {
    
    
    public static void main(String[] args) {
    
    
        BinaryTree binaryTree = new BinaryTree();
        Node a = new Node("A");
        Node b = new Node("B");
        Node c = new Node("C");
        Node e = new Node("E");
        Node f = new Node("F");
        Node g = new Node("G");
        //手动创建树
        binaryTree.setRoot(a);
        a.left=b;
        a.right=c;
        b.left=e;
        b.right=f;
        c.left=g;
        System.out.println("前序遍历:");
        binaryTree.preOrderM();
        System.out.println("中序遍历:");
        binaryTree.infixOrderM();
        System.out.println("后序遍历:");
        binaryTree.postOrderM();
    }
}
//创建树的属性和方法
class BinaryTree{
    
    
    //定义根节点
    Node root;

    public void setRoot(Node root) {
    
    
        this.root = root;
    }
    //前序遍历
    public void preOrderM() {
    
    
        if (this.root != null) {
    
    
            this.root.preOrder();
        } else {
    
    
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void infixOrderM() {
    
    
        if (this.root != null) {
    
    
            this.root.infixOrder();
        } else {
    
    
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void postOrderM() {
    
    
        if (this.root != null) {
    
    
            this.root.postOrder();
        } else {
    
    
            System.out.println("二叉树为空!无法遍历!");
        }
    }
}

//创建节点对象的属性和方法
class  Node{
    
    
    String name;
    Node left;
    Node right;

    public Node(String name) {
    
    
        this.name = name;
    }
    //重写toString方法
    @Override
    public String toString() {
    
    
        return "Node{" +
                "name='" + name + '\'' +
                '}';
    }

    //前序遍历
    public  void preOrder(){
    
    
        //先打印父节点
        System.out.println(this);
        if (this.left!=null){
    
    
            this.left.preOrder();
        }
        if (this.right!=null){
    
    
            this.right.preOrder();
        }
    }

    public void infixOrder() {
    
    
        //先进行左树遍历
        if (this.left!=null){
    
    
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right!=null){
    
    
            this.right.infixOrder();
        }
    }

    public void postOrder() {
    
    
        //先进行左树遍历
        if (this.left!=null){
    
    
            this.left.postOrder();
        }
        if (this.right!=null){
    
    
            this.right.postOrder();
        }
        System.out.println(this);
    }
}
Preorder traversal inorder traversal Postorder traversal
insert image description here insert image description here insert image description here

Binary tree delete node or tree

  • If the deleted node is a leaf node, delete the node
  • If the deleted node is a non-leaf node, delete the subtree

Code implementation: delete tree B and leaf node E

package Tree;
public class BinaryTreeDemo {
    
    
    public static void main(String[] args) {
    
    
        BinaryTree binaryTree = new BinaryTree();
        Node a = new Node("A");
        Node b = new Node("B");
        Node c = new Node("C");
        Node e = new Node("E");
        Node f = new Node("F");
        Node g = new Node("G");
        //手动创建树
        binaryTree.setRoot(a);
        a.left=b;
        a.right=c;
        b.left=e;
        b.right=f;
        c.left=g;
        System.out.println("前序遍历:");
        binaryTree.preOrderM();
        //删除某个节点
        System.out.println("删除节点E");
        binaryTree.deleteNode("E");
        System.out.println("查看删除后的节点");
        binaryTree.preOrderM();
        System.out.println("删除树B");
        binaryTree.deleteNode("B");
        System.out.println("查看删除后的节点");
        binaryTree.preOrderM();
    }
}
//创建树的属性和方法
class BinaryTree{
    
    
    //定义根节点
    Node root;

    public void setRoot(Node root) {
    
    
        this.root = root;
    }
    //前序遍历
    public void preOrderM() {
    
    
        if (this.root != null) {
    
    
            this.root.preOrder();
        } else {
    
    
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void infixOrderM() {
    
    
        if (this.root != null) {
    
    
            this.root.infixOrder();
        } else {
    
    
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void postOrderM() {
    
    
        if (this.root != null) {
    
    
            this.root.postOrder();
        } else {
    
    
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //删除节点,先判断根节点是不是所需要的节点
    public  void deleteNode(String name){
    
    
       if (this.root!=null){
    
    
           if (this.root.name==name){
    
    
               this.root=null;
           }else {
    
    
               this.root.delNode(name);
           }
       }else {
    
    
           System.out.println("二叉树为空!无法删除节点");
       }
    }
}
//创建节点对象的属性和方法
class  Node{
    
    
    String name;
    Node left;
    Node right;

    public Node(String name) {
    
    
        this.name = name;
    }
    //重写toString方法
    @Override
    public String toString() {
    
    
        return "Node{" +
                "name='" + name + '\'' +
                '}';
    }
    //前序遍历
    public  void preOrder(){
    
    
        //先打印父节点
        System.out.println(this);
        if (this.left!=null){
    
    
            this.left.preOrder();
        }
        if (this.right!=null){
    
    
            this.right.preOrder();
        }
    }
    public void infixOrder() {
    
    
        //先进行左树遍历
        if (this.left!=null){
    
    
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right!=null){
    
    
            this.right.infixOrder();
        }
    }
    public void postOrder() {
    
    
        //先进行左树遍历
        if (this.left!=null){
    
    
            this.left.postOrder();
        }
        if (this.right!=null){
    
    
            this.right.postOrder();
        }
        System.out.println(this);
    }
    //删除node方法
    public void delNode(String name) {
    
    
        if (this.left!=null&&this.left.name==name){
    
    
            this.left=null;
            return;
        }
        if (this.right!=null&&this.right.name==name){
    
    
            this.right=null;
            return;
        }
        //上述是root底下的两个节点。若这两个都不是我们要的那个节点,需再递归
        if (this.left!=null){
    
    
            this.left.delNode(name);
        }
        if (this.right!=null){
    
    
            this.right.delNode(name);
        }
    }
}

6. Sequentially store binary trees

 顺序存储二叉树是二叉树的一种存储方式。
 将二叉树存储在一个数组中,通过存储元素的下标反映元素之间的父子关系。

insert image description here
Features:

  • Sequential storage of binary trees usually only considers complete binary trees
  • When traversing the array arr, it is still possible (pre-order, mid-order, post-order traversal)
  • The left child node of the element with subscript n is subscript 2*n+1
  • The right child node of the element with subscript n is subscript 2*n+2
  • The parent node of the element with index n is (n-1)/2

Sequential storage binary tree traversal (preorder, inorder, postorder)

public class ArrayBinaryTreeDemo {
    
    
    public static void main(String[] args) {
    
    
        String [] arr={
    
    "A","B","C","E","F","G"};
        ArrayBinaryTree tree = new ArrayBinaryTree(arr);
        tree.preOrder();
        System.out.println();
        tree.infixOrder();
        System.out.println();
        tree.postOrder();
    }
}
//实现顺序存储二叉树
class ArrayBinaryTree{
    
    
    String[] arr;

    public ArrayBinaryTree(String[] arr) {
    
    
        this.arr = arr;
    }
    public  void  preOrder(){
    
    
        this.preOrder(0);
    }
    public  void infixOrder(){
    
    
        this.infixOrder(0);
    }
    public  void  postOrder(){
    
    
        this.postOrder(0);
    }
    //实现前序遍历
    public  void  preOrder(int index){
    
    
        if (arr==null || arr.length==0){
    
    
            System.out.println("数组为空");
            return;
        }
        System.out.print(arr[index]+" ");
        if ((index * 2 + 1) < arr.length) {
    
    
            preOrder(index * 2 + 1);
        }
        //再递归右子树
        if ((index * 2 + 2) < arr.length) {
    
    
            preOrder(index * 2 + 2);
        }
    }
    //实现中序遍历
    public  void  infixOrder(int index){
    
    
        if (arr==null || arr.length==0){
    
    
            System.out.println("数组为空");
            return;
        }
        if ((index * 2 + 1) < arr.length) {
    
    
            infixOrder(index * 2 + 1);
        }
        System.out.print(arr[index]+" ");
        //再递归右子树
        if ((index * 2 + 2) < arr.length) {
    
    
            infixOrder(index * 2 + 2);
        }
    }
    //后序遍历
    public  void  postOrder(int index){
    
    
        if (arr==null || arr.length==0){
    
    
            System.out.println("数组为空");
            return;
        }
        if ((index * 2 + 1) < arr.length) {
    
    
            postOrder(index * 2 + 1);
        }
        //再递归右子树
        if ((index * 2 + 2) < arr.length) {
    
    
            postOrder(index * 2 + 2);
        }
        System.out.print(arr[index]+" ");
    }
}

operation result:
insert image description here

7. Threaded binary tree

对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。
根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。

Note: The clue linked list solves the problem of being unable to directly find the predecessor and successor nodes of the node in a certain traversal sequence, and solves the difficulty of finding the left and right children of the binary linked list.

Traversing the threaded binary tree: Because the pointing of each node changes after threading, the original traversal method cannot be used. At this time, a new method needs to be used to traverse the threaded binary tree. Each node can be traversed in a linear manner, so there is no need to use recursion. method, which also improves the efficiency of traversal.

In-order clue binary tree

For example: in-order clue binary tree

insert image description here

public class ThreadedBinaryTreeDemo {
    
    
    public static void main(String[] args) {
    
    
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        MyNode a = new MyNode("A");
        MyNode b = new MyNode("B");
        MyNode c = new MyNode("C");
        MyNode e = new MyNode("E");
        MyNode f = new MyNode("F");
        MyNode g = new MyNode("G");
        //手动创建树
        threadedBinaryTree.setRoot(a);
        a.left=b;
        a.right=c;
        b.left=e;
        b.right=f;
        c.left=g;
        System.out.println("未线索化前e节点的前驱节点和后驱");
        System.out.println("F号结点的前驱结点为:"+e.left);//3
        System.out.println("F号结点的后继结点为:"+e.right);//1
        System.out.println("中序线索化后e节点的前驱节点和后驱");
        threadedBinaryTree.infixThreadedNodes();
        System.out.println("F号结点的前驱结点为:"+e.left);//3
        System.out.println("F号结点的后继结点为:"+e.right);//1
    }
}
//定义能实现线索化的二叉树
class ThreadedBinaryTree {
    
    
    MyNode root;
    MyNode pre=null;//指向当前节点的前驱节点  递归过程中pre总是保留前一个节点
    //为了实现线索化,需要创建指向当前节点的前驱结点的指针
    public void setRoot(MyNode root) {
    
    
        this.root = root;
    }
    public void infixThreadedNodes() {
    
    
        this.infixThreadedNodes(root);
    }
    //编写对二叉树进行中序线索化的方法
    public void infixThreadedNodes(MyNode node) {
    
    
        if (node == null) {
    
    //节点为空 不能线索化
            return;
        }
            //线索化左子树
            infixThreadedNodes(node.left);
            if (node.left==null){
    
    
                node.left=pre;
                node.leftType=1;
            }
            //处理后继节点
            if (pre!=null && pre.right==null){
    
    
                pre.right=node;
                pre.rightType=1;
            }
            //每处理一个节点,让当前节点是下一个节点的前驱节点
            pre=node;
            //线索化右子树
            infixThreadedNodes(node.right);
    }
}
class  MyNode{
    
    
    String name;
    MyNode left;
    MyNode right;
    //说明
    //1.如果leftType==0 表示指向的是左子树,为1 表示指向前驱节点
    //2.如果rightType==0 表示指向的是右子树,为1 表示指向后继节点
    int leftType;
    int rightType;
    public MyNode(String name) {
    
    
        this.name = name;
    }
    //重写toString方法
    @Override
    public String toString() {
    
    
        return "Node{" +
                "name='" + name + '\'' +
                '}';
    }
}

operation result:
insert image description here

preorder clue binary tree

insert image description here

 //编写对二叉树进行前序线索化的方法
    public void preThreadedNodes(MyNode node) {
    
    
        if (node == null) {
    
    
            return;
        }
        //线索化当前节点
        //处理前驱节点     左指针为空 则将左指针指向前驱节点
        if (node.left == null) {
    
    
            node.left=pre;
            node.leftType=1;
        }
        //处理后继节点     前一个节点的后继节点指向当前节点
        if (pre != null && pre.right == null) {
    
    
            pre.right=node;
            pre.rightType=1;
         }
        //更新pre
        pre = node;
        //线索化左子树
        if (node.leftType == 0) {
    
    
            preThreadedNodes(node.left);
        }
        //线索化右子树
        if (node.rightType == 0) {
    
    
            preThreadedNodes(node.right);
        }
    }

insert image description here

postorder clue binary tree

insert image description here

 //编写对二叉树进行后序线索化的方法
    public void postThreadedNodes(MyNode node) {
    
    
        if (node == null) {
    
    
            return;
        }
        //线索化左子树
        if (node.leftType == 0) {
    
    
            postThreadedNodes(node.left);
        }
        //线索化右子树
        if (node.rightType == 0) {
    
    
            postThreadedNodes(node.right);
        }
        //线索化当前节点
        if (node.left == null) {
    
    
            node.left=pre;
            node.leftType=1;
        }
        if (pre != null && pre.right == null) {
    
    
            pre.right=node;
            pre.rightType=1;
        }
        //更新pre
        pre = node;
    }

insert image description here

Guess you like

Origin blog.csdn.net/qq_45637894/article/details/131046585