`
sunwinner
  • 浏览: 202640 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

二叉搜索树的节点删除算法

阅读更多

删除节点是二叉搜索树操作中最复杂的,但对有些应用又非常重要,有必要好好研究一下。

 

一个取巧的办法:在树节点中加入一个boolean字段,比如isDeleted。需要删除一个节点时就把这个字段置为true,其他操作比如find()在查找之前先判断这个节点是否已经标记为删除了,这样删除的节点将不会改变树的结构,当然这样做还会继续保留这种已经删除的节点。对某些应用场景,这种做法是有优势的,比如已经离职的员工档案要保留在员工记录中。

 

下面来实现这个删除节点的算法。首先列出树节点的数据结构:

 

/**
 * @author Sun Kui
 */
public class Node {
    // node key
    public int iData;
    // node value
    public double dData;
    public Node leftChild;
    public Node rightChild;

    public Node() {
        this(0, 0.0);
    }

    public Node(int id, double dd) {
        this.iData = id;
        this.dData = dd;
    }

    public void displayNode() {
        System.out.print('{');
        System.out.print(iData);
        System.out.print(", ");
        System.out.print(dData);
        System.out.print("} ");
    }
    
    @Override
    public String toString() {
        return iData + "=>" + dData;
    }
}
 

 

 

删除节点首先从查找要删除的节点入手:

 

        Node cur = root, parent = root;
        boolean isLeftChild = true;

        while (cur.iData != key) {
            parent = cur;
            if (key < cur.iData) {
                isLeftChild = true;
                cur = cur.leftChild;
            } else {
                isLeftChild = false;
                cur = cur.rightChild;
            }

            // no node to delete
            if (cur == null) {
                return false;
            }
        }
 

 

 

找到节点后,有三中可能的情况需要考虑:

    1.该节点是叶子节点

    2.该节点有一个子节点

    3.该节点有两个子节点

 

情况1:

要删除叶子节点,只需要改变该节点的父节点对应子字段的值,代码如下:

 

       if (cur.leftChild == null && cur.rightChild == null) {
            if (cur == root) {
                // remove root node
                root = null;
            } else if (isLeftChild) {
                cur.leftChild = null;
            } else {
                cur.rightChild = null;
            }
        }

 

 

情况2:

要删除的节点有一个子节点(左子节点或者右子节点),要从二叉搜索树中”剪断“这个节点,并把它的子节点连接到它的父节点上,这个过程要求改变父节点的适当引用,指向要删除的子节点。一共有4种情况:要删除的子节点可能有左子节点或者右子节点,并且每种情况中的要删除节点也可能是自己父节点的左子节点或者右子节点。另外还有一种特殊情况:被删除的节点可能是根节点,它没有父节点,只是被合适的子树代替。代码如下:

 

       else if (cur.leftChild == null) {
            // left child node is null
            if (cur == root) {
                root = cur.rightChild;
            } else if (isLeftChild) {
                parent.leftChild = cur.rightChild;
            } else {
                parent.rightChild = cur.rightChild;
            }
        } else if (cur.rightChild == null) {
            // right child node is null
            if (cur == root) {
                root = cur.leftChild;
            } else if (isLeftChild) {
                parent.leftChild = cur.leftChild;
            } else {
                parent.rightChild = cur.leftChild;
            }
        }
 

 

情况3:

要删除有两个子节点的节点,要用其中序后继节点来代替该节点。找后继节点的算法如下:首先找到初始节点的右子节点,它的关键字值一定比初始节点大。然后转到初始节点的右子节点的左子节点(如果有的话),然后继续到该左子节点的左子节点,顺着左子节点的路径一直向下找。这个路径的最后一个左子节点就是初始节点的后继。代码如下:

 

/**
     * Returns node with next highest value after node to delete goes to right
     * child, then right child's left descendants
     * 
     * This method assume that the node to delete has right child, for it's has
     * been checked in previous in delete() method.
     * 
     * @param node
     *            to delete
     */
    private Node getSuccessor(Node delNode) {
        Node successorParent = delNode;
        Node successor = delNode;
        // go to right child of node to delete
        Node current = delNode.rightChild;

        // loop until no more left children
        while (current != null) {
            successorParent = successor;
            successor = current;
            // go to left child
            current = current.leftChild;
        }

        // if successor is not right child, make connections,prepared for deletion
        if (successor != delNode.rightChild) {
            successorParent.leftChild = successor.rightChild;
            successor.rightChild = delNode.rightChild;
        }

        return successor;
    }

 

找到后继节点以后,后继节点可能与要删除的节点有两种位置关系:后继节点可能是要删除节点的右子节点,也可能是其左子孙节点。如果是右子节点情况就简单了一些,只需要把以后继节点为根的子数移到要删除节点的位置。否则要进行更复杂的操作,情形3的完整代码如下:

 

    else {
            // two children, so replace with in-order successor
            // get successor
            Node successor = getSuccessor(cur);

            // connect parent of current to successor instead
            if (cur == root) {
                root = successor;
            } else if (isLeftChild) {
                parent.leftChild = successor;
            } else {
                parent.rightChild = successor;
            }
            // connect successor to current's left child
            successor.leftChild = cur.leftChild;
        }
        return true;
 

请注意,getSuccessor()的代码中,已经完整了删除的部分操作。

 

最后贴出二叉搜索树删除节点算法的完整代码:

 

public boolean delete(int key) {
        Node cur = root, parent = root;
        boolean isLeftChild = true;

        while (cur.iData != key) {
            parent = cur;
            if (key < cur.iData) {
                isLeftChild = true;
                cur = cur.leftChild;
            } else {
                isLeftChild = false;
                cur = cur.rightChild;
            }

            // no node to delete
            if (cur == null) {
                return false;
            }
        }

        if (cur.leftChild == null && cur.rightChild == null) {
            if (cur == root) {
                // remove root node
                root = null;
            } else if (isLeftChild) {
                cur.leftChild = null;
            } else {
                cur.rightChild = null;
            }
        } else if (cur.leftChild == null) {
            // left child node is null
            if (cur == root) {
                root = cur.rightChild;
            } else if (isLeftChild) {
                parent.leftChild = cur.rightChild;
            } else {
                parent.rightChild = cur.rightChild;
            }
        } else if (cur.rightChild == null) {
            // right child node is null
            if (cur == root) {
                root = cur.leftChild;
            } else if (isLeftChild) {
                parent.leftChild = cur.leftChild;
            } else {
                parent.rightChild = cur.leftChild;
            }
        } else {
            // two children, so replace with in-order successor
            // get successor
            Node successor = getSuccessor(cur);

            // connect parent of current to successor instead
            if (cur == root) {
                root = successor;
            } else if (isLeftChild) {
                parent.leftChild = successor;
            } else {
                parent.rightChild = successor;
            }
            // connect successor to current's left child
            successor.leftChild = cur.leftChild;
        }
        return true;
    }

 

测试代码如下:

import java.util.Random;

/**
 * @author Sun Kui
 */
public class Main {
    public static void main(String... args) {
        BinarySearchTree theTree = new BinarySearchTree();
        if (args.length < 1) {
            System.out.println("Usage: java Main number");
            return;
        }

        int count = Integer.parseInt(args[0]);
        Random rand = new Random();
        for (int i = 0; i < count; i++) {
            theTree.insert(rand.nextInt(count * count), rand.nextDouble() + 1);
        }
        
        theTree.insert(35, 1.8);

        Node found = theTree.find(35);
        System.out.println(found);
        theTree.inOrder(theTree.getRootNode());
    }
}
 

 end.

 

分享到:
评论

相关推荐

    实现二叉排序树的各种算法

    二叉排序树(Binary Sort Tree,BST),也称为二叉查找树或二叉搜索树,是一种自平衡的二叉树数据结构,它在处理搜索、插入和删除操作时具有较高的效率。二叉排序树的主要特性是:对于任意节点,其左子树中的所有...

    C++二叉搜索树的插入和删除例子

    在二叉搜索树中,对于任何一个节点,其左子树中的所有节点的键都小于该节点的键,而右子树中的所有节点的键都大于该节点的键。这种特性使得二叉搜索树在查找、插入和删除操作上具有较高的效率。 `bstree.cpp`和`...

    二叉搜索树算法(共两个PPT)

    二叉搜索树(Binary Search Tree,BST),是数据结构领域中的一个重要概念,它是一种特殊的二叉树,每个节点的左子树只包含比其小的元素,而右子树则包含大于它的元素。这种特性使得二叉搜索树在查找、插入和删除...

    VC/C++实现二叉搜索树查找算法

    下面是一个简单的二叉搜索树节点模板类的示例: ```cpp template struct TreeNode { T value; TreeNode* left; TreeNode* right; }; ``` 接下来,我们可以实现查找算法。在二叉搜索树中查找一个值,我们从根...

    最优二叉搜索树

    最优二叉搜索树,也称为最优化二叉查找树或者动态二叉搜索树,是计算机科学中的一个重要概念,尤其在算法设计与分析领域占据一席之地。这种数据结构主要用于提高查询效率,在某些特定操作序列下,它能比普通二叉搜索...

    0020算法笔记——【动态规划】最优二叉搜索树问题 - liufeng_king的专栏 - 博客频道 - CSDN1

    【最优二叉搜索树问题】是一种在数据结构和算法领域中的经典问题,主要涉及动态规划的概念。该问题的目标是设计一棵二叉搜索树(BST),使得在给定的元素集合和存取概率分布下,搜索元素时的平均比较次数达到最小。 ...

    最优二叉搜索树算法及代码

    最优二叉搜索树,也称为最优化二叉查找树或动态二叉查找树,是一种特殊的二叉树数据结构,它的每个节点都包含一个键值和两个子节点,左子节点的键值小于当前节点,右子节点的键值大于当前节点。在最优二叉搜索树中,...

    二叉搜索树查找算法【实现】.rar_C++_C++ 二叉搜索树_organizationzme

    4. **删除算法**:删除二叉搜索树中的某个节点。情况复杂,包括删除叶子节点、只有一个子节点的节点以及有两个子节点的节点。 ```cpp TreeNode* deleteNode(TreeNode* root, int key) { // ... 实现删除逻辑 ... }...

    acm二叉搜索树参考代码

    在二叉搜索树中,对于任意节点而言,其左子树中的所有节点的键值都小于该节点的键值,而右子树中的所有节点的键值都大于该节点的键值。这种特性使得二叉搜索树在查找、插入和删除操作上具有较高的效率,尤其是在树...

    二叉排序树的基本算法 数据结构实验代码

    根据给定文件的信息,我们...通过以上内容的学习,可以进一步加深对指针、动态变量的理解,同时掌握二叉树及其存储结构的特点和应用,特别是二叉搜索树的相关算法。这对于后续学习更复杂的数据结构和算法非常有帮助。

    二叉搜索树vc6.0运行ok代码

    2. **删除操作**:从二叉搜索树中移除指定节点。删除操作相对复杂,因为它涉及三种情况:要删除的节点没有子节点、有一个子节点和有两个子节点。没有子节点的情况直接删除即可;有一个子节点时,用子节点替换要删除...

    面试题27_二叉搜索树与双向链表

    同时,为了不创建新节点,我们需要直接修改原二叉搜索树节点的prev和next指针。 在实际编程实现时,通常会用到栈或递归的方法。使用栈时,我们可以从根节点开始,将大于当前节点的右子节点压入栈中,然后弹出栈顶...

    二叉搜索树的源码,加上注释和自己理解

    在二叉搜索树中,对于任意节点,其左子树中的所有节点的键都小于该节点的键,而右子树中的所有节点的键都大于该节点的键。这种特性使得二叉搜索树在插入、删除和查找操作上具有较高的效率。 在提供的源码中,我们...

    二叉搜索树 查找 构造 插入 删除

    二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树数据结构,其中每个节点包含一个键值(key),且满足以下条件:对于任意节点而言,其左子树中的所有节点的键值均小于该节点的键值;其右子树中的所有节点的键值...

    二叉搜索树_二叉搜索树_源码

    总之,二叉搜索树是一种重要的数据结构,它提供了快速的查找、插入和删除功能,广泛应用于各种算法和数据管理中。通过分析和理解“二叉搜索树.c”的源代码,我们可以深入学习二叉搜索树的原理和实现细节,这对于学习...

    二叉搜索树算法

    在二叉搜索树中,对于任意节点,其左子树中的所有节点的键都小于该节点的键,而右子树中所有节点的键都大于该节点的键。这种特性使得二叉搜索树在查找、插入和删除操作上具有较高的效率。 在给定的文件列表中,我们...

    最优二叉搜索树模范讲解

    **最优二叉搜索树**(Optimal Binary Search Tree,简称OBST)是一种特殊的二叉搜索树,其节点按照特定的方式排列,使得在进行搜索操作时所需的平均成本最小。在实际应用中,构建一个最优的二叉搜索树能够有效提高数据...

    二叉搜索树的C++源代码

    在二叉搜索树中,对于任何一个节点,其左子树中的所有节点的键都小于该节点的键,而右子树中的所有节点的键都大于该节点的键。这种特性使得二叉搜索树非常适合进行查找、插入和删除等操作。 在提供的压缩包文件中,...

    最优二叉搜索树的动态规划算法

    在这个问题中,我们用C#语言实现了动态规划算法来构建最优二叉搜索树。动态规划是一种解决最优化问题的有效方法,它将大问题分解为小问题,然后通过构建子问题的最优解来获得原问题的最优解。 首先,我们需要理解...

    霍夫曼树和二叉搜索树代码实现

    这种性质使得在二叉搜索树中查找、插入和删除操作的时间复杂度在平均情况下为O(log n),其中n是树中节点的数量。 在C++中实现二叉搜索树,一般会定义一个二叉树节点类(如代码中的`BinaryTreeNode`),包含数据成员...

Global site tag (gtag.js) - Google Analytics