在二进制搜索树中的顺序继承者

时间:2011-03-29 11:25:09

标签: algorithm data-structures tree binary-search-tree

给定BST中的节点,如何找到下一个更高的密钥?

18 个答案:

答案 0 :(得分:67)

一般方法取决于您的节点中是否有父链接。

如果存储父链接

然后你选择:

  1. 如果您当前的节点有正确的孩子,则为正确孩子的最左边的孩子。如果合适的孩子没有留下孩子,那么正确的孩子就是你的继承人。
  2. 向上导航父祖先节点,当您找到其子节点是您当前所在节点的父节点时,父节点是原始节点的顺序后继节点。
  3. 如果您有正确的孩子,请采用这种方法(上述案例1):

    inorder-when-right-child

    如果您没有合适的孩子,请采用以下方法(上述案例2):

    inorder-when-no-right-child

    如果您不存储父链接

    然后,您需要对树进行完整扫描,跟踪节点,通常使用堆栈,这样您就可以获得与依赖父链接的第一种方法基本相同的信息。 / p>

答案 1 :(得分:6)

Lasse的answer的Python代码:

def findNext(node):
  if node.rightChild != None:
    return findMostLeft(node.rightChild)
  else:
    parent = node.parent
    while parent != None:
      if parent.leftChild == node:
        break
      node = parent
      parent = node.parent
    return parent

答案 2 :(得分:2)

这是一个不需要父链接或中间结构(如堆栈)的实现。这个有序后继函数与大多数人可能正在寻找的有点不同,因为它在键上操作而不是节点。此外,它将找到密钥的后继,即使它不存在于树中。但是,如果你需要,不要太难改变。

public class Node<T extends Comparable<T>> {

private T data;
private Node<T> left;
private Node<T> right;

public Node(T data, Node<T> left, Node<T> right) {
    this.data = data;
    this.left = left;
    this.right = right;
}

/*
 * Returns the left-most node of the current node. If there is no left child, the current node is the left-most.
 */
private Node<T> getLeftMost() {
    Node<T> curr = this;
    while(curr.left != null) curr = curr.left;
    return curr;
}

/*
 * Returns the right-most node of the current node. If there is no right child, the current node is the right-most.
 */
private Node<T> getRightMost() {
    Node<T> curr = this;
    while(curr.right != null) curr = curr.right;
    return curr;
}

/**
 * Returns the in-order successor of the specified key.
 * @param key The key.
 * @return
 */
public T getSuccessor(T key) {
    Node<T> curr = this;
    T successor = null;
    while(curr != null) {
        // If this.data < key, search to the right.
        if(curr.data.compareTo(key) < 0 && curr.right != null) {
            curr = curr.right;
        }
        // If this.data > key, search to the left.
        else if(curr.data.compareTo(key) > 0) { 
            // If the right-most on the left side has bigger than the key, search left.
            if(curr.left != null && curr.left.getRightMost().data.compareTo(key) > 0) {
                curr = curr.left;
            }
            // If there's no left, or the right-most on the left branch is smaller than the key, we're at the successor.
            else {
                successor = curr.data;
                curr = null;
            }
        }
        // this.data == key...
        else {
            // so get the right-most data.
            if(curr.right != null) {
                successor = curr.right.getLeftMost().data;
            }
            // there is no successor.
            else {
                successor = null;
            }
            curr = null;
        }
    }
    return successor;
}

public static void main(String[] args) {
    Node<Integer> one, three, five, seven, two, six, four;
    one = new Node<Integer>(Integer.valueOf(1), null, null);
    three = new Node<Integer>(Integer.valueOf(3), null, null);
    five = new Node<Integer>(Integer.valueOf(5), null, null);
    seven = new Node<Integer>(Integer.valueOf(7), null, null);
    two = new Node<Integer>(Integer.valueOf(2), one, three);
    six = new Node<Integer>(Integer.valueOf(6), five, seven);
    four = new Node<Integer>(Integer.valueOf(4), two, six);
    Node<Integer> head = four;
    for(int i = 0; i <= 7; i++) {
        System.out.println(head.getSuccessor(i));
    }
}
}

答案 3 :(得分:2)

使用二进制搜索树,找到给定节点的下一个最高节点的算法基本上是找到该节点的右子树的最低节点。

算法可以简单:

  1. 从给定节点的正确子节点开始(使其成为临时当前节点)
  2. 如果当前节点没有左子节点,则它是下一个最高节点。
  3. 如果当前节点有一个左子节点,则将其设为当前节点。
  4. 重复2和3,直到找到下一个最高节点。

答案 4 :(得分:2)

点击此处:InOrder Successor in a Binary Search Tree

  

在二叉树中,一个序列的继承者   node是Inorder中的下一个节点   遍历二叉树。为了   对于最后一个节点,Successor为NULL   Inoorder遍历。在二进制搜索中   Tree,Inorder输入的继承者   节点也可以定义为节点   最小的密钥大于   输入节点的键。

答案 5 :(得分:1)

C ++解决方案,假设节点有左,右和父指针:

这说明函数Node* getNextNodeInOrder(Node)按顺序返回二叉搜索树的下一个键。

#include <cstdlib>
#include <iostream>
using namespace std;

struct Node{
    int data;
    Node *parent;
    Node *left, *right;
};

Node *createNode(int data){
    Node *node =  new Node();
    node->data = data;
    node->left = node->right = NULL;
    return node;
}

Node* getFirstRightParent(Node *node){
    if (node->parent == NULL)
        return NULL;

    while (node->parent != NULL && node->parent->left != node){
        node = node->parent;
    }
    return node->parent;
}
Node* getLeftMostRightChild(Node *node){
    node = node->right;
    while (node->left != NULL){
        node = node->left;
    }
    return node;
}
Node *getNextNodeInOrder(Node *node){
    //if you pass in the last Node this will return NULL
    if (node->right != NULL)
        return getLeftMostRightChild(node);
    else
        return getFirstRightParent(node);
}
void inOrderPrint(Node *root)
{
    if (root->left != NULL) inOrderPrint(root->left);
    cout << root->data << " ";
    if (root->right != NULL) inOrderPrint(root->right);
}

int main(int argc, char** argv) {
    //Purpose of this program is to demonstrate the function getNextNodeInOrder
    //of a binary tree in-order.  Below the tree is listed with the order
    //of the items in-order.  1 is the beginning, 11 is the end.  If you 
    //pass in the node 4, getNextNode returns the node for 5, the next in the 
    //sequence.

    //test tree:
    //
    //        4
    //      /    \
    //     2      11
    //    / \     /
    //   1  3    10
    //          /
    //         5
    //          \
    //           6 
    //            \
    //             8
    //            / \
    //           7  9


    Node *root = createNode(4);
    root->parent = NULL;

    root->left = createNode(2);
    root->left->parent = root;

    root->right = createNode(11);
    root->right->parent = root;

    root->left->left = createNode(1);
    root->left->left->parent = root->left;

    root->right->left = createNode(10);
    root->right->left->parent = root->right;

    root->left->right = createNode(3);
    root->left->right->parent = root->left;

    root->right->left->left = createNode(5);
    root->right->left->left->parent = root->right->left;

    root->right->left->left->right = createNode(6);
    root->right->left->left->right->parent = root->right->left->left;

    root->right->left->left->right->right = createNode(8);
    root->right->left->left->right->right->parent = 
            root->right->left->left->right;

    root->right->left->left->right->right->left = createNode(7);
    root->right->left->left->right->right->left->parent = 
            root->right->left->left->right->right;

    root->right->left->left->right->right->right = createNode(9);
    root->right->left->left->right->right->right->parent = 
            root->right->left->left->right->right;

    inOrderPrint(root);

    //UNIT TESTING FOLLOWS

    cout << endl << "unit tests: " << endl;

    if (getNextNodeInOrder(root)->data != 5)
        cout << "failed01" << endl;
    else
        cout << "passed01" << endl;

    if (getNextNodeInOrder(root->right) != NULL)
        cout << "failed02" << endl;
    else
        cout << "passed02" << endl;

    if (getNextNodeInOrder(root->right->left)->data != 11)
        cout << "failed03" << endl;
    else
        cout << "passed03" << endl;

    if (getNextNodeInOrder(root->left)->data != 3)
        cout << "failed04" << endl;
    else
        cout << "passed04" << endl;

    if (getNextNodeInOrder(root->left->left)->data != 2)
        cout << "failed05" << endl;
    else
        cout << "passed05" << endl;

    if (getNextNodeInOrder(root->left->right)->data != 4)
        cout << "failed06" << endl;
    else
        cout << "passed06" << endl;

    if (getNextNodeInOrder(root->right->left->left)->data != 6)
        cout << "failed07" << endl;
    else
        cout << "passed07" << endl;

    if (getNextNodeInOrder(root->right->left->left->right)->data != 7)
        cout << "failed08 it came up with: " << 
          getNextNodeInOrder(root->right->left->left->right)->data << endl;
    else
        cout << "passed08" << endl;

    if (getNextNodeInOrder(root->right->left->left->right->right)->data != 9)
        cout << "failed09 it came up with: " 
          << getNextNodeInOrder(root->right->left->left->right->right)->data 
          << endl;
    else
        cout << "passed09" << endl;

    return 0;
}

打印哪些:

1 2 3 4 5 6 7 8 9 10 11

unit tests: 
passed01
passed02
passed03
passed04
passed05
passed06
passed07
passed08
passed09

答案 6 :(得分:1)

如果我们执行顺序遍历,那么我们访问左子树,然后访问根节点,最后访问树中每个节点的右子树。 执行顺序遍历将以递增的顺序为我们提供二叉搜索树的键,因此当我们参考检索属于二叉搜索树的节点的顺序后,我们的意思是序列中的下一个节点是什么给定的节点。

让我们说我们有一个节点R,我们希望它顺序继承我们会有以下情况。

[1] 根R有一个正确的节点,所以我们需要做的就是遍历R-> right的最左边节点。

[2] 根R没有正确的节点,在这种情况下,我们遍历父链接之后的树,直到节点R是其父节点的左子节点,当发生这种情况时将父节点P作为顺序继承者。

[3] 我们处于树的最右边节点,在这种情况下没有顺序继承。

该实现基于以下节点定义

class node
{
private:
node* left;
node* right;
node* parent
int data;

public:
//public interface not shown, these are just setters and getters
.......
};

//go up the tree until we have our root node a left child of its parent
node* getParent(node* root)
{
    if(root->parent == NULL)
        return NULL;

    if(root->parent->left == root)
        return root->parent;
    else
        return getParent(root->parent);
}

node* getLeftMostNode(node* root)
{
    if(root == NULL)
        return NULL;

    node* left = getLeftMostNode(root->left);
    if(left)
        return left;
    return root;
}

//return the in order successor if there is one.
//parameters - root, the node whose in order successor we are 'searching' for
node* getInOrderSucc(node* root)
{
    //no tree, therefore no successor
    if(root == NULL)
        return NULL;

    //if we have a right tree, get its left most node
    if(root->right)
        return getLeftMostNode(root->right);
    else
        //bubble up so the root node becomes the left child of its
        //parent, the parent will be the inorder successor.
        return getParent(root);
}

答案 7 :(得分:1)

我们不需要父链接或堆栈即可在O(log n)(假设平衡树)中找到顺序后继者。 保留一个临时变量,该遍历遍历遍历中遇到的最新值大于键。如果有序遍历发现该节点没有合适的子节点,则这将是有序后继。否则,是右子的最左子孙。

答案 8 :(得分:0)

好吧,我来试一试,因为每个人都在发布他们的实现。该技术来自《算法导论》一书。基本上,您需要一种从子节点回溯到父节点的方法。

首先,你需要一个类来表示节点:

public class TreeNode<V extends Comparable<V>> {
    TreeNode<V> parent;
    TreeNode<V> left;
    TreeNode<V> right;
    V data;

    public TreeNode(TreeNode<V> parent, V data) {
        this.parent = parent;
        this.data = data;
    }

    public void insert(TreeNode<V> parent, V data) {
        if (data.compareTo(this.data) < 0) {
            if (left == null) {
                left = new TreeNode<>(parent, data);
            } else {
                left.insert(left, data);
            }
        } else if (data.compareTo(this.data) > 0) {
            if (right == null) {
                right = new TreeNode<>(parent, data);
            } else {
                right.insert(right, data);
            }
        }
        // ignore duplicates
    }

    @Override
    public String toString() {
        return data + " -> [parent: " + (parent != null ? parent.data : null) + "]";
    }
}

您可以使用另一个类来运行操作:

public class BinarySearchTree<E extends Comparable<E>> {

    private TreeNode<E> root;

    public void insert(E data) {
        if (root == null) {
            root = new TreeNode<>(null, data);
        } else {
            root.insert(root, data);
        }
    }

    public TreeNode<E> successor(TreeNode<E> x) {
        if (x != null && x.right != null) {
            return min(x.right);
        }
        TreeNode<E> y = x.parent;
        while (y != null && x == y.right) {
            x = y;
            y = y.parent;
        }
        return y;
    }

    public TreeNode<E> min() {
        return min(root);
    }

    private TreeNode<E> min(TreeNode<E> node) {
        if (node.left != null) {
            return min(node.left);
        }
        return node;
    }

    public TreeNode<E> predecessor(TreeNode<E> x) {
        if(x != null && x.left != null) {
            return max(x.left);
        }

        TreeNode<E> y = x.parent;
        while(y != null && x == y.left) {
            x = y;
            y = y.parent;
        }
        return y;
    }

    public TreeNode<E> max() {
        return max(root);
    }

    private TreeNode<E> max(TreeNode<E> node) {
        if (node.right != null) {
            return max(node.right);
        }
        return node;
    }

}

寻找访问者的想法是:

  • 如果右子树不为空,求右子树中的最小值。
  • 否则,继续向上爬树,直到到达一个节点,该节点是其父节点的左子节点。

对于寻找前任,反之亦然。

您可以在我的 GitHub in this package 上找到完整的工作示例。

答案 9 :(得分:0)

Node successor(int data) {
  return successor(root, data);
}

// look for the successor to data in the tree rooted at curr
private Node successor(Node curr, int data) {
  if (curr == null) {
    return null;
  } else if (data < curr.data) {
    Node suc = successor(curr.left, data);
    // if a successor is found use it otherwise we know this node
    // is the successor since the target node was in this nodes left subtree
    return suc == null ? curr : suc;
  } else if (data > curr.data) {
    return successor(curr.right, data);
  } else {
    // we found the node so the successor might be the min of the right subtree
    return findMin(curr.right);
  }
}

private Node findMin(Node curr) {
  if (curr == null) {
    return null;
  }

  while (curr.left != null) {
    curr = curr.left;
  }

  return curr;
}

答案 10 :(得分:0)

我们可以在不使用父指针的情况下找到O(log n)中的后继(对于平衡树)。

这个想法非常类似于你有父指针的时候。

我们可以定义一个递归函数,实现如下:

  • 如果当前节点是目标节点,则返回其右子树的最左侧/最小节点(如果存在)。
  • 如果目标小于当前节点,则向左递归,如果目标小于当前节点,则向右递归。
  • 如果目标位于左侧,但尚未找到后继节点,则返回当前节点。

的伪代码:

Key successor(Node current, Key target):
   if current == null
      return null
   if target == current.key
      if current.right != null
         return leftMost(current.right).key
      else
         return specialKey
   else
      if target < current.key
         s = successor(current.left, target)
         if s == specialKey
            return current.key
         else
            return s
      else
         return successor(current.right, target)

Node leftMost(Node current):
    while current.left != null
       current = current.left
    return current

Live Java demo

答案 11 :(得分:0)

C#实现(非递归!)在二叉搜索树中查找给定节点的“下一个”节点,其中每个节点都有一个指向其父节点的链接。

    public static Node WhoIsNextInOrder(Node root, Node node)
    {
        if (node.Right != null)
        {
            return GetLeftMost(node.Right);
        }
        else
        {
            Node p = new Node(null,null,-1);
            Node Next = new Node(null, null, -1);
            bool found = false;
            p = FindParent(root, node);
            while (found == false)
                {
                    if (p.Left == node) { Next = p; return Next; }
                    node = p;
                    p = FindParent(root, node);
                }
            return Next;
        }
    }

    public static Node FindParent(Node root, Node node)
    {
        if (root == null || node == null)
        {
            return null;
        }
        else if ( (root.Right != null && root.Right.Value == node.Value) || (root.Left != null && root.Left.Value == node.Value))
        {
            return root;
        }
        else
        {
            Node found = FindParent(root.Right, node);

            if (found == null)
            {
                found = FindParent(root.Left, node);
            }

            return found;
        }
    }

    public static Node GetLeftMost (Node node)
    {
        if (node.Left == null)
        {
            return node;
        }
        return GetLeftMost(node.Left);
    }

答案 12 :(得分:0)

我在Google上查看的每个“教程”以及此主题中的所有答案都使用以下逻辑:“如果节点没有正确的子节点,则其有序后继将是其祖先之一。父链接继续向上移动,直到你得到其父节点的左子节点。然后这个父节点将是有序后继节点。

这与思考“如果我的父母比我大,那么我就是左孩子”(二元搜索树的属性)一样。这意味着您可以简单地向上走父链,直到上面的属性为真。在我看来,这会产生更优雅的代码。

我想每个人都通过查看分支而不是使用父链接的代码路径中的值来检查“我是左孩子”的原因来自于“借用”逻辑link-to-parent算法。

同样从下面的代码中我们可以看到不需要堆栈数据结构,如其他答案所示。

以下是一个简单的C ++函数,适用于两种用例(使用和不使用父级链接)。

Node* nextInOrder(const Node *node, bool useParentLink) const
{
    if (!node)
        return nullptr;

    // when has a right sub-tree
    if (node->right) {
        // get left-most node from the right sub-tree
        node = node->right;
        while (node->left)
            node = node->left;
        return node;
    }

    // when does not have a right sub-tree
    if (useParentLink) {
        Node *parent = node->parent;
        while (parent) {
            if (parent->value > node->value)
                return parent;
            parent = parent->parent;
        }
        return nullptr;
    } else {
        Node *nextInOrder = nullptr;
        // 'root' is a class member pointing to the root of the tree
        Node *current = root;
        while (current != node) {
            if (node->value < current->value) {
                nextInOrder = current;
                current = current->left;
            } else {
                current = current->right;
            }
        }
        return nextInOrder;
    }
}

Node* previousInOrder(const Node *node, bool useParentLink) const
{
    if (!node)
        return nullptr;

    // when has a left sub-tree
    if (node->left) {
        // get right-most node from the left sub-tree
        node = node->left;
        while (node->right)
            node = node->right;
        return node;
    }

    // when does not have a left sub-tree
    if (useParentLink) {
        Node *parent = node->parent;
        while (parent) {
            if (parent->value < node->value)
                return parent;
            parent = parent->parent;
        }
        return nullptr;
    } else {
        Node *prevInOrder = nullptr;
        // 'root' is a class member pointing to the root of the tree
        Node *current = root;
        while (current != node) {
            if (node->value < current->value) {
                current = current->left;
            } else {
                prevInOrder = current;
                current = current->right;
            }
        }
        return prevInOrder;
    }
}

答案 13 :(得分:0)

我们可以将其分为3种情况:

  1. 如果节点是父节点:在这种情况下,我们发现它是否有一个正确的节点并遍历到右节点的最左边的子节点。如果右节点没有子节点,则右节点是其顺序后继节点。如果没有正确的节点,我们需要向上移动树以找到顺序继承者。

  2. 如果节点是左子节点:在这种情况下,父节点是inorder后继节点。

  3. 如果节点(称之为x)是一个右子(它的直接父节点):我们遍历树,直到找到一个左子树有x的节点。

  4. 极端情况:如果节点是最右边的节点,则没有顺序后继节点。

答案 14 :(得分:0)

在Java中执行此操作

<div id="container">
  <h2 class="header">Restaunt Name:</h2
  ><h2 class="header">Phone #:</h2
  ><h2 class="header">Star Rating:</h2>
</div>

答案 15 :(得分:0)

JavaScript解决方案 - 如果给定节点具有正确的节点,则返回右子树中的最小节点 - 如果没有,那么有两种可能性: - 给定节点是父节点的左子节点。如果是,则返回父节点。否则,给定节点是父节点的右子节点。如果是,则返回父节点的右子节点

function nextNode(node) {
  var nextLargest = null;
  if (node.right != null) {
    // Return the smallest item in the right subtree

    nextLargest = node.right;
    while (nextLargest.left !== null) {
      nextLargest = nextLargest.left;
    }

    return nextLargest;
  } else {
    // Node is the left child of the parent
    if (node === node.parent.left) return node.parent;

    // Node is the right child of the parent
    nextLargest = node.parent;
    while (nextLargest.parent !== null && nextLargest !== nextLargest.parent.left) {
      nextLargest = nextLargest.parent
    }
    return nextLargest.parent;
  }
}

答案 16 :(得分:0)

这些答案对我来说都显得过于复杂。我们真的不需要父指针或任何辅助数据结构,如堆栈。我们需要做的就是按顺序从根遍历树,一旦找到目标节点就设置一个标志,我们访问的树中的下一个节点将是顺序后继节点。这是我写的一个快速而肮脏的例程。

Node* FindNextInorderSuccessor(Node* root, int target, bool& done)
{
    if (!root)
        return NULL;

    // go left
    Node* result = FindNextInorderSuccessor(root->left, target, done);
    if (result)
        return result;

    // visit
    if (done)
    {
        // flag is set, this must be our in-order successor node
        return root;
    }
    else
    {
        if (root->value == target)
        {
            // found target node, set flag so that we stop at next node
            done = true;
        }
    }

    // go right
    return FindNextInorderSuccessor(root->right, target, done);
}

答案 17 :(得分:0)

您可以阅读其他信息here(Rus lung)

Node next(Node x)
   if x.right != null
      return minimum(x.right)
   y = x.parent
   while y != null and x == y.right
      x = y
      y = y.parent
   return y


Node prev(Node x)
   if x.left != null
      return maximum(x.left)
   y = x.parent
   while y != null and x == y.left
      x = y
      y = y.parent
   return y