【发布时间】:2011-02-06 10:17:34
【问题描述】:
如何在 BST 中找到第 N 个最大的节点?
在执行 BST 的按顺序遍历时是否保留计数变量?当count = N时返回元素???
【问题讨论】:
标签: algorithm binary-tree
如何在 BST 中找到第 N 个最大的节点?
在执行 BST 的按顺序遍历时是否保留计数变量?当count = N时返回元素???
【问题讨论】:
标签: algorithm binary-tree
提示:使用树的中序遍历。它可以按排序顺序打印出项目,因此您可以确定找到第 N 个最大的项目。在“行走”时保留一个计数器,每次“访问”一个节点时递增。
编辑:虽然 IVlad 的回答确实更快,但它需要您在节点中保留额外的信息。这个答案不是,而是O(n)。只是指出这是您必须注意的权衡。
【讨论】:
查看我的回答here。您可以平均在O(log n) 中执行此操作,其中 n = 节点数。如果树不平衡,最坏的情况仍然是O(n)(如果它是平衡的,则总是O(log n))。然而,为了遍历总是O(n)。
【讨论】:
使用倒序遍历。即先到右孩子而不是左孩子。 递归可以得到如下: 考虑递归解决方案时必须使用全局计数的最重要问题。
reverseInorder(root){
if(root!=null){
reverseInorder(root->rightChild);
self
reverseInorder(root->leftChild);
}
}
java中的解决方案
package datastructure.binaryTree;
import datastructure.nodes.BinaryTreeNode;
public class NthElementFromEnd {
private BinaryTree tree=null;
int currCount=0;
public NthElementFromEnd(int[] dataArray) {
this.tree=new BinaryTree(dataArray);
}
private void getElementFromEnd(int n){
getElementFromEnd(this.tree.getRoot(),n);
}
private void getElementFromEnd(BinaryTreeNode node,int n){
if(node!=null){
if(currCount<n)
getElementFromEnd(node.getRightChild(),n);
currCount++;
if(currCount==n)
{
System.out.print(" "+node.getData());
return;
}
if(currCount<n)
getElementFromEnd(node.getLeftChild(),n);
}
}
public static void main(String args[]){
int data[]={1,2,3,4,5,6,7,8,9};
int n=2;
new NthElementFromEnd(data).getElementFromEnd(n);
}
}
【讨论】:
int nLargeBST(node *root, int N) {
if (!root || N < 0) {
return -1;
}
nLargeBST(root->left, N);
--N;
if(N == 0) {
return root->val;
}
nLargeBST(root->right, N);
}
【讨论】:
这段代码来自我的作业,其中一个条件是不使用 数组。为了使代码更紧凑和可读,您可以使用 字符串名称.split("|")。由于该方法是递归的,我使用 stringBuilder 它具有以下结构:“counter|orderOfElementToFind|dataInrequiredNode”
protected StringBuilder t(StringBuilder s)
{
if (lc != null)
{
lc.t(s);
}
if((s.toString().charAt(s.toString().length() - 1)) == '|')
{
String str = s.toString();
s.delete(0, s.length());
int counter = 0, k = 0;
String strTemp = "", newStrBuilContent = "";
for (int i = 0, c = 0 ; i < str.length(); ++i)
{
if (c == 0)
{
if (str.charAt(i) != '|')
{
strTemp += str.charAt(i);
}
else
{
counter = Integer.parseInt(strTemp);
++c;
strTemp = "";
}
}
else
{
if (str.charAt(i) != '|')
{
strTemp += str.charAt(i);
}
else
{
k = Integer.parseInt(strTemp);
}
}
counter ++;
newStrBuilContent = (counter + "|" + k + "|");
s.append(newStrBuilContent);
if (counter == k)
{
double ldata = this.getData();
s.append(ldata);
}
}
if (rc != null)
{
rc.t(s);
}
return s;
}
以及方法调用:
// the value of counter ad the beginning is 0 and data
// segment is missing
String s = ("0|" + order +"|");
StringBuilder strBldr = new StringBuilder(s);
String content = sTree.t(strBldr).toString();
s = "";
for (int i = 0, c = 0; i < content.length(); ++i)
{
if (c < 2)
{
if (content.charAt(i) == '|')
{
++c;
}
}
else
{
s += content.charAt(i);
}
}
`
【讨论】:
您可以通过对二叉搜索树的中序遍历稍作修改来做到这一点 - 我们正在寻找第 k 个最大的元素;
void kthLargest(Node node, int k, int count) {
if(node != null) {
nthLargest(node.left,k,count); //traversing the left node
//while visit the node we do the following
count++; // increment the count and check if that is equal to k
if ( count == k ) {
System.out.println("Node found "+node.value);
}
nthLargest(node.right,k,count); //traversing the right node
}
}
但是这样的问题是你将到达第 k 个最小的元素,因此你的方法调用应该是这样的:第 k 个最大的元素 = (n-k)th 个最小的元素。
nthLargest(root,n-k,0);
【讨论】:
这个想法很简单:按照每个节点的值的递减顺序遍历树。当您到达第 N 个节点时,打印该节点值。这是递归代码。
void printNthNode(Node* root, int N)
{
if(root == NULL)
return;
static int index = 0; //These will initialize to zero only once as its static
//For every Node go to the right of that node first.
printNthNode(root->right, N);
//Right has returned and now current node will be greatest
if(++index == N)
{
printf("%d\n", root->data);
return;
}
//And at last go to the left
printNthNode(root->left, N);
}
编辑 -
根据下面的 cmets,由于静态局部变量,这看起来像是一次性调用函数。这可以通过为index 传递包装对象来解决,如下所示:
class WrapIndex {
public: int index;
};
并且方法签名将更改为
void printNthNode(Node* root, int N, WrapIndex wrapInd)
现在,我们不需要局部静态变量;而是使用包装对象的index。调用看起来像
WrapIndex wrapInd = new WrapIndex();
wrapInd.index=0;
printNthNode(root,7,wrapInd);
wrapInd.index=0;
printNthNode(root,2,wrapInd);
【讨论】:
one-time 仅供使用的功能。例如,如果 b 是一个 BSTree,printNthNode(b.head, 7); // prints the 7th largest number printNthNode(b.head, 2); // fails to print anything
static 局部变量。我们可以为index 使用包装器对象,并在每次调用时传递它。对于每个新调用,我们都会将此包装对象的index 设置为0。
wrapInd的引用,否则可能无法正常工作。或者,只需一个整数引用变量作为第三个参数printNthNode(Node*, int, int&) 就足够了。电话将是int counter = 0; printNthNode (root, 7, counter); counter = 0; printNthNode(root, 2, counter);
保持每个节点的子树大小(root.size 类似的东西)。例如 {2,3,1} 是根为 2 的二叉树,则节点 (2) 的大小为 3,节点 (1) 的大小为 1,节点 (2) 的大小为 1
如果你想在根节点大小为 23 的树中找到第 4 个最大的元素,请考虑它的排名
最大元素等级为 23,因为根节点大小为 23。所以第 4 大元素等级为 23-4+1=20
所以我们必须在给定的树中找到排名第 20 的元素
最初将 rank=0 标志声明为零
从根节点开始找到它的等级(等级+左孩子的大小+1),例如左孩子的大小是16,那么根元素的等级是17(等级+左孩子的大小+1)
所以我们必须寻找排名为 20 的元素。显然我们必须遍历它的右孩子
遍历右孩子,根据上式求右孩子rank(根据上式,注意:现在rank flag值为17),根据rank决定是向右还是向左
【讨论】:
BST 中的第 K 个最大元素。学习如何思考此类问题并使用递归解决。 Kth Larget Explanation Recursion
【讨论】:
我会通过从最大元素到最小元素遍历树并在到达询问位置时返回值来做到这一点。我为第二大价值实施了类似的任务。 2 的值是硬编码的,但是否可以通过附加参数轻松更改:)
void BTree::findSecondLargestValueUtil(Node* r, int &c, int &v)
{
if(r->right) {
this->findSecondLargestValueUtil(r->right, c, v);
}
c++;
if(c==2) {
v = r->value;
return;
}
if(r->left) {
this->findSecondLargestValueUtil(r->left, c, v);
}
}
int BTree::findSecondLargestValue()
{
int c = 0;
int v = -1;
this->findSecondLargestValueUtil(this->root, c, v);
return v;
}
【讨论】:
// C++ program to find k'th largest element in BST
#include<iostream>
using namespace std;
struct Node
{
int key;
Node *left, *right;
};
// A utility function to create a new BST node
Node *newNode(int item)
{
Node *temp = new Node;
temp->key = item;
temp->left = temp->right = NULL;
return temp;
}
// A function to find k'th largest element in a given tree.
void kthLargestUtil(Node *root, int k, int &c)
{
// Base cases, the second condition is important to
// avoid unnecessary recursive calls
if (root == NULL || c >= k)
return;
// Follow reverse inorder traversal so that the
// largest element is visited first
kthLargestUtil(root->right, k, c);
// Increment count of visited nodes
c++;
// If c becomes k now, then this is the k'th largest
if (c == k)
{
cout << "K'th largest element is "
<< root->key << endl;
return;
}
// Recur for left subtree
kthLargestUtil(root->left, k, c);
}
// Function to find k'th largest element
void kthLargest(Node *root, int k)
{
// Initialize count of nodes visited as 0
int c = 0;
// Note that c is passed by reference
kthLargestUtil(root, k, c);
}
/* A utility function to insert a new node with given key in BST */
Node* insert(Node* node, int key)
{
/* If the tree is empty, return a new node */
if (node == NULL) return newNode(key);
/* Otherwise, recur down the tree */
if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
/* return the (unchanged) node pointer */
return node;
}
// Driver Program to test above functions
int main()
{
/* Let us create following BST
50
/ \
30 70
/ \ / \
20 40 60 80 */
Node *root = NULL;
root = insert(root, 50);
insert(root, 30);
insert(root, 20);
insert(root, 40);
insert(root, 70);
insert(root, 60);
insert(root, 80);
int c = 0;
for (int k=1; k<=7; k++)
kthLargest(root, k);
return 0;
}
【讨论】:
Swift 版本。这与 Vallabh Patade 所说的密切相关。当它试图通过一个没有子节点的节点时,计数器加 1。和他的有点不一样。
class BinaryNode {
var val: Int
var left: BinaryNode?
var right: BinaryNode?
init(value: Int) {
self.val = value
}
}
func findMaxValue(_ n: Int, from root: BinaryNode?) {
var counter = 0
maxValue(counter: &counter, n: n, node: root)
}
private func maxValue(counter: inout Int, n: Int, node: BinaryNode?) {
if node == nil {
counter += 1
return
}
maxValue(counter: &counter, n: n, node: node?.right)
// If the counter has reached the nth node we're looking for.
if counter == n {
if let val = node?.val { print(val) }
}
maxValue(counter: &counter, n: n, node: node?.left)
}
【讨论】: