[Graphical Data Structure] Binary Search Tree

[TOC]

binary search tree definition

The value of the head node of each subtree is greater than the value of all nodes in its left subtree, and it is also smaller than the value of all nodes in its right subtree.

The inorder traversal sequence of a binary search tree must be arranged from small to large.

Binary search tree node definition

/// <summary>
/// 二叉查找树节点
/// </summary>
public class Node
{
    /// <summary>
    /// 节点值
    /// </summary>
    public int Data { get; set; }
    /// <summary>
    /// 左子节点
    /// </summary>
    public Node Left { get; set; }
    /// <summary>
    /// 右子节点
    /// </summary>
    public Node Right { get; set; }

    /// <summary>
    /// 打印节点值
    /// </summary>
    public void DisplayNode()
    {
        Console.Write(Data + " ");
    }
}

insert node

The operation of inserting a node in a binary search tree is relatively simple, and it is only necessary to find the position where the node to be inserted is placed.

The overall process of inserting a node:

  1. Set the parent node to the current node, the root node.
  2. If the data value in the new node is less than the data value in the current node, then set the current node as the left child of the current node. If the data value in the new node is greater than the data value in the current node, then skip to step 4.

  3. If the value of the left child of the current node is null, insert the new node here and exit the loop. Otherwise, skip to the next loop operation of the while loop.

  4. Sets the current node as the right child of the current node.
  5. If the value of the right child of the current node is null, insert the new node here and exit the loop. Otherwise, skip to the next loop operation of the while loop.

Code:

public class BinarySearchTree
{
    public Node root;
    public BinarySearchTree()
    {
        root = null;
    }
    /// <summary>
    /// 二叉查找树插入结点
    /// </summary>
    /// <param name="i"></param>
    public void Insert(int i)
    {
        Node newNode = new Node
        {
            Data = i
        };
        if (root == null)
        {
            root = newNode;
        }
        else
        {
            Node current = root;
            Node parent;
            while (true)
            {
                parent = current;
                if (i < current.Data)
                {
                    current = current.Left;
                    if (current == null)
                    {
                        parent.Left = newNode;
                        break;
                    }
                }
                else
                {
                    current = current.Right;
                    if (current == null)
                    {
                        parent.Right = newNode;
                        break;
                    }
                }
            }
        }
    }
}

Because the in-order traversal sequence of a binary search tree must be arranged from small to large, we can test the insertion operation of a binary search tree through in-order traversal. Regarding the binary tree traversal operation, you can move to my last blog [Graphic Data Structure] Binary tree traversal .

In-order traversal code implementation:

/// <summary>
/// 二叉查找树中序遍历
/// </summary>
/// <param name="node"></param>
public void InOrder(Node node)
{
    if (node != null)
    {
        InOrder(node.Left);
        node.DisplayNode();
        InOrder(node.Right);
    }
}

Test code:

class BinarySearchTreeTest
{
    static void Main(string[] args)
    {
        BinarySearchTree bst = new BinarySearchTree();
        bst.Insert(23);
        bst.Insert(45);
        bst.Insert(16);
        bst.Insert(37);
        bst.Insert(3);
        bst.Insert(99);
        bst.Insert(22);         

        Console.WriteLine("中序遍历: ");
        bst.InOrder(bst.root);

        Console.ReadKey();
    }
}

Test Results:mark

The above test code forms a binary search tree like this:

mark

find node

There are three easiest things to do with a binary search tree (BST): find a particular number, find the minimum value, and find the maximum value.

find the minimum value

According to the nature of the binary search tree, the minimum value of the binary search tree must be the leftmost child node of the left subtree.

So the implementation is very simple, that is, starting from the root node to find the leftmost child node of the left subtree of the binary search tree.

Code:

/// <summary>
/// 查找二叉查找树最小值
/// </summary>
/// <returns></returns>
public int FindMin()
{
    Node current = root;
    while (current.Left != null)
    {
        current = current.Left;
    }
    return current.Data;
}

Find the maximum value

According to the nature of the binary search tree, the maximum value of the binary search tree must be the rightmost child node of the right subtree.

So the implementation is very simple, that is, starting from the root node to find the rightmost child node of the right subtree of the binary search tree.

Code:

/// <summary>
/// 查找二叉查找树最大值
/// </summary>
/// <returns></returns>
public int FindMax()
{
    Node current = root;
    while (current.Right != null)
    {
        current = current.Right;
    }
    return current.Data;
}

Find a specific value

According to the nature of the binary search tree, starting from the root node, compare the size of the specific value and the value of the root node. If it is greater than the value of the root node, it means that the specific value is on the right subtree of the root node, and continue to perform this operation on the right child node; if it is smaller than the value of the root node, it means that the specific value is on the left subtree of the root node. , continue doing this on the left child. If no node value equal to the specified value is found until the execution is complete, then there is no node containing this specified value in the binary search tree.

Code:

/// <summary>
/// 查找二叉查找树特定值节点
/// </summary>
/// <param name="key">特定值</param>
/// <returns></returns>
public Node Find(int key)
{
    Node current = root;
    while (current.Data != key)
    {
        if (key < current.Data)
        {
            current = current.Left;
        }
        if (key > current.Data)
        {
            current = current.Right;
        }
        // 如果已到达 BST 的末尾
        if (current == null)
        {
            return null;
        }
    }
    return current;
}

delete node

Compared with the previous operation, the operation of deleting a node in a binary search tree is more complicated, because deleting a node will risk destroying the correct
hierarchical order of the BST.

We all know that nodes in a binary search tree can be divided into: nodes with no children, nodes with one child, and nodes with two children. Then we can simply split the delete node operation of the binary search tree to facilitate our understanding. As shown below:

mark

delete leaf node

Deleting leaf nodes is the easiest thing to do. The only thing to do is to set a child node of the parent node of the target node to null.

Check whether the left child node and right child node of this node are empty (null), if both are empty (null), it indicates that it is a leaf node.

Then check if this node is the root node. If so, set it to null.

Otherwise, if isLeftChild is true, set the parent node's left child node to null (null); if isLeftChild is false, set the parent node's right child node to null (null).

Code:

//要删除的结点是叶子结点的处理
if (current.Left == null && current.Right == null)
{
    if (current == root)
        root = null;
    else if (isLeftChild)
        parent.Left = null;
    else
    {
        parent.Right = null;
    }
}

delete a node with one child

When the node to be deleted has a child node, four conditions need to be checked:

  1. The children of this node may be left children;
  2. The child node of this node may be the right child node;
  3. The node to be deleted may be the left child;
  4. The node to be deleted may be the right child.

mark

Code:

//要删除的结点是带有一个子节点的节点的处理
//首先判断子结点是左子节点还是右子节点,然后再判断当前节点是左子节点还是右子节点
else if (current.Right == null)
    if (current == root)
        root = current.Left;
    else if (isLeftChild)
        parent.Left = current.Left;
    else
        parent.Right = current.Left;
else if (current.Left == null)
    if (current == root)
        root = current.Right;
    else if (isLeftChild)
        parent.Left = current.Right;
    else
        parent.Right = current.Right;

delete a node with two children

If you want to delete the node marked 52, you need to rebuild the tree. There is no way to replace it with a subtree starting at 54, because 54 already has a left child. The answer to this question is to move the in-order successor node to the position where the node is to be deleted. Of course, it is also necessary to distinguish whether the successor node itself has child nodes.

markmark

Here we need to understand the definition of successor nodes .

The successor of a node refers to the next node in the in-order traversal sequence of this node. Correspondingly, the predecessor node refers to the previous node of this node in the in-order traversal sequence.

For example, the in-order traversal sequence of the binary tree in the following figure is: DBEAFCG, then the successor node of A is F, and the predecessor node of A is E.

mark

Knowing this, the operation of deleting a node with two child nodes can be transformed into finding the successor node of the node to be deleted and assigning the right subtree of the node to be deleted to the right child node of the successor node . It should be noted here that if If the successor node itself has child nodes, you need to assign the child node of the successor node to the left child node of the parent node of the successor node.

First, let's get the code of the subsequent node, and then give an example to illustrate:

/// <summary>
/// 获取后继结点
/// </summary>
/// <param name="delNode">要删除的结点</param>
/// <returns></returns>
public Node GetSuccessor(Node delNode)
{
    //后继节点的父节点
    Node successorParent = delNode;
    //后继节点
    Node successor = delNode.Right;
    Node current = delNode.Right.Left;
    while (current != null)
    {
        successorParent = successor;
        successor = current;
        current = current.Left;
    }
    //如果后继结点不是要删除结点的右子结点,
    //则要将后继节点的子结点赋给后继节点父节点的左节点
    //删除结点的右子结点赋给后继结点作为 后继结点的后继结点
    if (successor != delNode.Right)
    {
        successorParent.Left = successor.Right;
        successor.Right = delNode.Right;
    }
    return successor;
}

Code implementation to delete a node with two children:

//要删除的结点是带有两个子节点的节点的处理
else
{
    Node successor = GetSuccessor(current);
    if (current == root)
        root = successor;
    else if (isLeftChild)
        parent.Left = successor;
    else
        parent.Right = successor;
    //因为后继结点是要删除结点右子树的最左侧结点
    //所以后继结点的左子树肯定是要删除结点左子树
    successor.Left = current.Left;
}

We observe that the successor of the deleted node must be the leftmost node of the right subtree of the deleted node . There are 3 cases here:

Successor nodes are children of the deleted node

mark

The node 37 is deleted, and the successor node 40 is a child node of the deleted node 37 . delNodeIt is node 37, it successoris node 40, it delNode.Rightis node 40, successor == delNode.Rightand the successor node is the child node of the deleted node. This situation is the simplest.

The successor node is not a child of the deleted node

mark

Successor node 38 is the leftmost node of the right subtree of deletion node 37 . delNodeIt's node 37, successorit's node 38, successorParentit's node 40, delNode.Rightit's node 40. successor != delNode.Right, so to successorParent.Left = successor.Right;successor.Right = delNode.Right;. Because successor.Right==null, so successorParent.Left = null. successor.Right = delNode.Right, node 40 becomes the right child of node 38. Because the successor node of the deleted node must be the leftmost node of the right subtree of the deleted node, the successor node must have no left child node . After the deleted node is deleted, the successor node will be added to the position of the deleted node. successor.Left = current.Left;, that is, the left child of the deleted node becomes the left child of the successor node.

The searched binary tree after finishing removing nodes becomes:

mark

The successor node is not a child of the deleted node and has children

mark

This case is similar to the previous case, the only difference is that the successor node has a child node (note that it must be the right child node). That is successorParent.Left = successor.Right;, the right child of the successor node becomes the left child of the parent node of the successor node. Since it successor.Rightis node 39, the left child of node 40 becomes node 39. Other operations are exactly the same as in the previous case.

The searched binary tree after finishing removing nodes becomes:

mark

The overall process of deleting a node operation:

  1. Assign the right child of the successor node as the left child of the parent node of the successor node.
  2. Assign the right child of the node to be deleted as the right child of the successor node.
  3. Remove the current node from the right child of the parent node and point it to the successor node.
  4. Removes the left child of the current node from the current node and points it to the left child of the successor node.

Combining the above three cases of deleting nodes, the complete code for deleting nodes is as follows:

/// <summary>
/// 二叉查找树删除节点
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Delete(int key)
{
    //要删除的当前结点
    Node current = root;
    //当前结点的父结点
    Node parent = root;
    //当前结点是否是左子树 
    bool isLeftChild = true;
    //先通过二分查找找出要删除的结点
    while (current.Data != key)
    {
        parent = current;
        if (key < current.Data)
        {
            isLeftChild = true;
            current = current.Left;
        }
        else
        {
            isLeftChild = false;
            current = current.Right;
        }
        if (current == null)
            return false;
    }


    //要删除的结点是叶子结点的处理
    if (current.Left == null && current.Right == null)
    {
        if (current == root)
            root = null;
        else if (isLeftChild)
            parent.Left = null;
        else
        {
            parent.Right = null;
        }
    }

    //要删除的结点是带有一个子节点的节点的处理
    else if (current.Right == null)
        if (current == root)
            root = current.Left;
        else if (isLeftChild)
            parent.Left = current.Left;
        else
            parent.Right = current.Left;
    else if (current.Left == null)
        if (current == root)
            root = current.Right;
        else if (isLeftChild)
            parent.Left = current.Right;
        else
            parent.Right = current.Right;

    //要删除的结点是带有两个子节点的节点的处理
    else
    {
        Node successor = GetSuccessor(current);
        if (current == root)
            root = successor;
        else if (isLeftChild)
            parent.Left = successor;
        else
            parent.Right = successor;
        //因为后继结点是要删除结点右子树的最左侧结点
        //所以后继结点的左子树肯定是要删除结点左子树
        successor.Left = current.Left;
    }
    return true;
}

/// <summary>
/// 获取后继结点
/// </summary>
/// <param name="delNode">要删除的结点</param>
/// <returns></returns>
public Node GetSuccessor(Node delNode)
{
    //后继节点的父节点
    Node successorParent = delNode;
    //后继节点
    Node successor = delNode.Right;
    Node current = delNode.Right.Left;
    while (current != null)
    {
        successorParent = successor;
        successor = current;
        current = current.Left;
    }
    //如果后继结点不是要删除结点的右子结点,
    //则要将后继节点的子结点赋给后继节点父节点的左节点
    //删除结点的右子结点赋给后继结点作为 后继结点的后继结点
    if (successor != delNode.Right)
    {
        successorParent.Left = successor.Right;
        successor.Right = delNode.Right;
    }
    return successor;
}

delete node test

We still use in-order traversal for testing, first constructing a binary search tree:

static void Main(string[] args)
{
    BinarySearchTree bst = new BinarySearchTree();
    bst.Insert(23);
    bst.Insert(45);
    bst.Insert(16);
    bst.Insert(37);
    bst.Insert(3);
    bst.Insert(99);
    bst.Insert(22);
    bst.Insert(40);
    bst.Insert(35);
    bst.Insert(38);
    bst.Insert(44);
    bst.Insert(39);
}          

The constructed binary search tree:

mark

The test is divided into three cases:

Test delete leaf nodes

delete leaf node 39

Console.Write("删除节点前: ");
bst.InOrder(bst.root);

bst.Delete(39);

Console.Write("删除节点后: ");
bst.InOrder(bst.root);

Test Results:mark

Test to delete a node with one child

delete node 38 with one child

Console.Write("删除节点前: ");
bst.InOrder(bst.root);

bst.Delete(38);

Console.Write("删除节点后: ");
bst.InOrder(bst.root);

Test Results:mark

Test deleting a node with two children

delete node 37 with two children

Console.Write("删除节点前: ");
bst.InOrder(bst.root);

bst.Delete(37);

Console.Write("删除节点后: ");
bst.InOrder(bst.root);

Test Results:mark

refer to:

"Data Structure and Algorithm C# Language Description"

"Dahua Data Structure"

"Data Structure and Algorithm Analysis C Language Description"

Everyone went out to be happy on May 1st, why do I have to code at home by myself, is it because of love? Is it because of responsibility? neither. It's because I only have learning in my heart (actually because of poverty ). Haha, I wish everyone a happy May Day in advance, and it’s fun to eat!




Source: http://songwenjie.cnblogs.com/
Disclaimer: This article is a summary of bloggers' learning and perceptions. The level is limited. If it is inappropriate, please correct me. If you think it is not bad, you may click the [ Recommended ] button below, thank you for your support. Please indicate the source when reprinting and citing.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325114687&siteId=291194637