您的位置:首页 > 理论基础 > 数据结构算法

二叉搜索树的插入和删除结点操作以及iterator的构造

2016-07-23 14:44 423 查看
二叉搜索树作为一种常用的数据结构,熟练掌握是很有必要的。其插入操作非常简答。但删除操作逻辑上就比较复杂了。下面总结一下增加和删除操作的逻辑以及如何构造二叉树的iterator。

二叉搜索树的插入结点操作

二叉搜索树的插入操作较为简单,只需要在插入的同时保证二叉搜索树的性质不改变即可。也就是说我们需要在插入之前先搜索到正确的插入位置,然后将新结点放入其中即可。

二叉搜索树的删除结点操作

相比插入结点操作,删除的操作就相对复杂一些了。总体来说,删除操作分为三种情况(将被删结点记为z):

1. 如果被z没有子树,则直接将z删除即可。从其父节点中将指向z的指针改为null。

2. 如果z只有一个子树,那么我们用这个子树代替z原来的位置。既将z的父节点中指向z的指针改为指向z的子树的根。

3. 如果z有两个子树,则我们先在z的右子树中找到z的后继结点y,然后用y代替原来的树中z的位置。z原来的右子树中除去y后余下的结点作为y的右子树。z原来的左子树作为y的左子树。这个情况还分为y是是z的右孩子和y不是z的右孩子两种情况分析。

那么实际应用的时候我们就应该考虑四种不同的情况。如下所示:

- 如果z没有左子树,那么我们用z的右子树来代替z。z的右子树可能是null也可能不是null。当z的右子树是null的时候,我们就处理了上述三种情况中的1,也就是z没有子树的情况。当z的右子树不是null的时候,我们就处理了上述三种情况中的2的一半,既z只有一个子树且该子树为z的右子树的情况。

- 如果z只有左子树,那么我们用z的左子树代替z。这样我们就处理了上述三种情况中2的另一半。

- 当z既有左子树又有右子树的时候,我们找到在z的右子树中找到z的后继结点y。根据二叉搜索树的性质我们知道y一定是没有左子树的。我们需要将y从现在的位置取出并将其替换到z现在的位置上。

如果y是z的右孩子,则直接用y替代z即可。z的左子树成为y的左子树。y的右子树不变

如果y不是z的右孩子,那么我们先用y的右孩子代替y,然后用y代替z。

上述四种情况图示如下(图片来自Introduction to Algorithms 3rd):



二叉树的iterator的构造

我们采用非递归DFS来进行遍历,需要使用一个stack来构造。为什么是stack?因为我们在遍历的时候需要记录最近访问过的上一个分叉结点并在当前branch遍历结束之后返回去遍历最近分叉结点的另一个branch。

二叉搜索树中的插入和删除结点以及iterator的Java代码如下:

public class TreeNode {
int val;
//You can either have a pointer pointing to its parent or not
//TreeNode parent;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}


import java.util.*

public class BST implements Iterable<TreeNode> {
private TreeNode root;

public BST (TreeNode root) {
this.root = root;
}

public TreeNode getRoot() {
return this.root;
}

public void insertNode(TreeNode root, TreeNode newNode) {
if (root == null) {
//If tree is a empty tree, let the new node be the root
root = newNode;
}
//Use parent variable to keep track of the node which is going to be the parent of the new node
TreeNode parent = null;
while (root != null) {
parent = root;
if (root.val > newNode.val) {
root = root.left;
}
else if (root.val < newNode.val) {
root = root.right;
}
else {
//The node we want to insert already exists.
return;
}
}
if (parent.val > newNode.val) {
parent.left = newNode;
}
else {
parent.right = newNode;
}
}

public void deleteNode(TreeNode root, TreeNode target) {
if (target.left == null) {
transplantTree(root, target, target.right);
}
else if (target.right == null) {
transplantTree(root, target, target.left);
}
else {
TreeNode y = treeMinimum(target.right);
if (target.right != y) {
transplantTree(root, y, y.right);
transplantTree(root, target, y);
y.right = target.right;
}
else {
transplantTree(root, target, y);
}
y.left = target.left;
}
}

private void transplantTree(TreeNode root, TreeNode old, TreeNode newNode) {
//A helper method used to replace the treenode old into treenode new within a tree rooted at root.
TreeNode parent_old = findParent(root, old);
if (old == root) {
root = newNode;
}
else if (parent_old.left == old) {
parent_old.left = newNode;
}
else {
parent_old.right = newNode;
}
}

public TreeNode treeMinimum(TreeNode node) {
//Find the Minimum value in the tree rooted at node
if (node.left != null) {
node = node.left;
}
return node;
}

public TreeNode findParent(TreeNode root, TreeNode node) {
//Find the parent of node within a Binary Search Tree which is rooted at root
TreeNode parent = null;
if (root.val == node.val) {
//Be careful about using "root == node" directly since they are all reference type variables in Java. Using "==" will compare their memory address rather than the actual value. They will only equal to each other if and only if they are pointing to the same object, which is not always the case.
return null;
}
else if (root.val > node.val) {
parent = findParent(root.left, node);
}
else {
parent = findParent(root.right, node);
}

return parent;
}

@Override
public Iterator<TreeNode> iterator() {
return new TreeIterator(this.root);
}

private static class TreeIterator implements java.util.Iterator<TreeNode> {
private TreeNode curr;
Deque<TreeNode> deque = new ArrayDeque<>();

public TreeIterator (TreeNode root) {
this.curr = root;
}

@Override
public boolean hasNext() {
return (curr != null || !deque.isEmpty());
}

@Override
public TreeNode next() {
if (this.hasNext()) {
while (curr != null) {
deque.push(curr);
curr = curr.left;
}

TreeNode node = deque.pop();
curr = node.right;

return node;
}

throw new NoSuchElementException();
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构