二叉搜索树及二叉树的前中后序遍历(递归和非递归)

定义

二叉查找树(Binary Search Tree)又称二叉排序树、二叉搜索树。二叉查找树是为了实现快速查找而生的。不过,它不仅仅支持快速查找一个数据,还支持快速插入、删除一个数据。二叉查找树要求,在树中的任意一个节点都要满足,其左子树中每个节点的值,都要小于这个节点的值,而右子树每个节点的值都大于这个节点的值。

这两个都是二叉查找树。

查找

在二叉查找树中查找一个节点,我们先取根节点,如果它等于我们要查找的数据,那就返回。如果要查找的数据比根节点的值小,那就左子树中递归查找;如果要查找的数据比根节点的值大,那就右子树中递归查找。

查找的代码实现

public class BinarySearchTree {
    
    
    private Node root;

    public Node find(int data) {
    
    
        Node temp = root;
        while (temp != null) {
    
    
            if (data == temp.data) {
    
    
                return temp;
            } else if(data > temp.data) {
    
    
                temp = temp.rchild;
            } else {
    
    
                temp = temp.lchild;
            }
        }
        return null;
    }
}

public class Node {
    
    
    int data;
    Node lchild;
    Node rchild;

    public Node(int data) {
    
    
        this.data = data;
    }
}

插入

插入的操作类似于查找的操作。从根节点开始,依次比较要插入的数据和节点的关系。
如果要插入的数据比节点的数据大,并且节点的右子树为空,就将数据直接插入到右子节点的位置,如果右子树不为空,那就继续遍历右子树;如果要插入的数据比节点的数据小,并且节点的左子树为空,就将数据直接插入到左子节点的位置,如果左子树不为空,那就继续遍历左子树。

插入的代码实现

public void insert(int data) {
    
    
        if (root == null) {
    
    
            root = new Node(data);
            return;
        }
        
        Node temp = root;
        while (true) {
    
    
            if (data > temp.data) {
    
    
                if (temp.rchild == null) {
    
    
                    temp.rchild = new Node(data);
                    return;
                }
                temp = temp.rchild;
            } else {
    
    
                if (temp.lchild == null) {
    
    
                    temp.lchild = new Node(data);
                    return;
                }
                temp = temp.lchild;
            }
        }
    }

删除

二叉查找树的查找和插入的操作比较简单,但是删除的操作较为复杂,分为三种情况。
第一种情况,要删除的节点是叶子节点。此时只需要直接将父节点中指向要删除节点的指针置为null即可。如下图中删除28。
第二种情况,要删除的节点有只有一个子节点(左子节点或右子节点),我们只需要更新父节点中的指针,让它指向要删除节点的字节点就可以了。如下图中的34。
第三种情况,要删除的节点有两个子节点。这种情况下,我们需要把要删除节点的右子树中最小的节点替换要删除节点。如下图中的15。


删除完成后

代码实现

public TreeNode deleteNode(TreeNode root, int key) {
    
    
    if (root == null) return null;

    if (root.val == key) {
    
    
        if (root.left == null) return root.right;
        if (root.right == null) return root.left;

        TreeNode minNode = getMin(root.right);
        root.val = minNode.val;
        root.right = deleteNode(root.right, minNode.val);
    } else if (root.val > key) {
    
    
        root.left = deleteNode(root.left, key);
    } else {
    
    
        root.right = deleteNode(root.right, key);
    }

    return root;
}

private TreeNode getMin(TreeNode root) {
    
    
    while (root.left != null) {
    
    
        root = root.left;
    }
    return root;
}

非递归实现:

 public void delete(int data) {
    
    
        Node temp = root;  // temp指向要删除的节点
        Node ftemp = null; // ftemp指向要删除节点的父节点

        while (temp != null && temp.data != data) {
    
    
            ftemp = temp;
            if (data > temp.data) {
    
    
                temp = temp.rchild;
            } else {
    
    
                temp = temp.lchild;
            }
        }

        if (temp == null) return; // 没有找到对应的节点

        // 若找到,temp就是要删除的节点
        // 要删除的节点有两个子节点
        if (temp.lchild != null && temp.rchild != null) {
    
    
            Node minTemp = temp.rchild;  // 存储右子树的最小节点
            Node fminTemp = temp; // minTemp的父节点

            // 找到右子树的最小节点
            while (minTemp.lchild != null) {
    
    
                fminTemp = minTemp;
                minTemp = minTemp.lchild;
            }

            temp.data = minTemp.data; // 将最小节点的值替换到temp中
            temp = minTemp;  // 变成删除叶子节点
            ftemp = fminTemp;
        }

        // 删除节点是叶子节点或者仅有一个节点
        Node child; // temp的子节点
        if (temp.lchild != null) {
    
    
            child = temp.lchild;
        } else if (temp.rchild != null) {
    
    
            child = temp.rchild;
        } else {
    
    
            child = null;
        }

        if (ftemp == null) {
    
      // 删除的是根节点
            root = child;
        } else if (ftemp.lchild == temp) {
    
    
            ftemp.lchild = child;
        } else {
    
    
            ftemp.rchild = child;
        }
    }

支持重复数据的二叉查找树

前面对二叉查找树的操作,针对的都是不存在重复数据的情况。如果需要存储重复数据,在插入数据时,如果碰到一个节点的值和要插入的值相等,我们把这个要插入的数据放在这个节点的右子树。也就是说,把这个新插入的数据当作大于这个节点的值来处理。

当要查找数据的时候,遇到值相同的节点,我们并不停止查找操作,而是继续在右子树中查找,直到遇到叶子节点,才停止。这样就可以把所有符合要求的节点都查出来。
删除节点时,也需要先查找到每个要删除的节点,然后按之前的删除操作依次删除。

查找最大最小节点

    public Node findMin() {
    
    
        if (root == null) return null;
        Node temp = root;
        while (temp.lchild != null) {
    
    
            temp = temp.lchild;
        }
        return temp;
    }

    public Node findMax() {
    
    
        if (root == null) return null;
        Node temp = root;
        while (temp.rchild != null) {
    
    
            temp = temp.rchild;
        }
        return temp;
    }

二叉树的遍历

前序、中序、后序遍历(递归)

中序遍历二叉查找树,可以输出有序的数据序列,时间复杂度是O(n),非常高效

    // 前序遍历
    public void preOrder(Node node) {
    
    
        if (node != null) {
    
    
            System.out.println(node.data);
            preOrder(node.lchild);
            preOrder(node.rchild);
        }
    }

    // 中序遍历
    public void inOrder(Node node) {
    
    
        if (node != null) {
    
    
            inOrder(node.lchild);
            System.out.println(node.data);
            inOrder(node.rchild);
        }
    }

    // 后序遍历
    public void postOrder(Node node) {
    
    
        if (node != null) {
    
    
            postOrder(node.lchild);
            postOrder(node.rchild);
            System.out.println(node.data);
        }
    }

前序、中序、后序遍历(非递归)

前序

    // 前序遍历
    public void preOrderTraversal(Node node) {
    
    
        if (node == null) return;

        Stack<Node> stack = new Stack<>();
        stack.push(node);

        while (!stack.isEmpty()) {
    
    
            node = stack.pop();
            if (node.rchild != null) {
    
    
                stack.push(node.rchild);
            }
            if (node.lchild != null) {
    
    
                stack.push(node.lchild);
            }
            System.out.println(node.data);
        }
    }

中序

    // 中序遍历
    public void inOrderTraversal(Node node) {
    
    
        Stack<Node> stack = new Stack<>();

        while (node != null || !stack.isEmpty()) {
    
    
            if (node != null) {
    
    
                stack.push(node);
                node = node.lchild;
            } else {
    
    
                node = stack.pop();
                System.out.println(node.data);
                node = node.rchild;
            }
        }
    }

后序

    // 后序遍历
    public void postOrderTraversal(Node node) {
    
    
        if (node == null) return;

        Stack<Node> stack1 = new Stack<>();
        Stack<Node> stack2 = new Stack<>();

        stack1.push(node);
        while (!stack1.isEmpty()) {
    
    
            node = stack1.pop();
            stack2.push(node);
            if (node.lchild != null) {
    
    
                stack1.push(node.lchild);
            }
            if (node.rchild != null) {
    
    
                stack1.push(node.rchild);
            }
        }
        while (!stack2.isEmpty()) {
    
    
            System.out.println(stack2.pop().data);
        }
    }

层序遍历

// 层序遍历
public void levelOrder(Node node) {
    
    
    if (node == null) return;

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

    while (!queue.isEmpty()) {
    
    
        node = queue.poll();
        System.out.println(node.data);
        if (node.lchild != null) {
    
    
            queue.offer(node.lchild);
        }
        if (node.rchild != null) {
    
    
            queue.offer(node.rchild);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_46122005/article/details/110895892