BSTでN番目に大きいノードを見つける方法は?
BSTの順序トラバーサルを実行している間、カウント変数を保持しますか? count = N ???の場合に要素を返します
私の答えを見てください ここ 。これは、平均してO(log n)
で実行できます。ここで、n =ノードの数です。最悪の場合でもO(n)
です。ツリーのバランスが取れていない場合(ただし、バランスが取れている場合は常にO(log n)
)です。ただし、トラバーサルは常にO(n)
です。
アイデアは非常に単純です。各ノードの値の降順でツリーをトラバースします。 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);
}
_
編集-以下のコメントによると、これは静的ローカル変数のため、1回限りの呼び出し関数のように見えます。これは、次のように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);
_
ヒント:ツリーのinorder traversalを使用します。アイテムを並べ替えて印刷できるため、N番目に大きいアイテムを確実に見つけることができます。ノードを「訪問」するたびにインクリメントしながら、「ウォーク」しながらカウンターを維持します。
編集:IVladの答えは確かに高速ですが、ノードに追加情報を保持する必要があります。この答えはそうではありませんが、それはO(n)
です。これは、注意する必要があるトレードオフであることを指摘してください。
各ノードでサブツリーのサイズを維持します(root.sizeのようなもの)。たとえば、{2,3,1}はルート2のバイナリツリーで、ノード(2)のサイズは3、ノード(1)のサイズは1、ノード(2)のサイズは1です。
ルートノードサイズ23のツリーで4番目に大きい要素を検索する場合は、そのランクについて考えます。
ルートノードのサイズが23であるため、最大要素ランクは23です。したがって、4番目に大きい要素ランクは23-4 + 1 = 20です。
そのため、指定されたツリーで20番目のランクの要素を見つける必要があります
最初にrank =フラグをゼロに宣言します
ルートノードから開始して、そのランク(ランク+左の子のサイズ+ 1)を見つけます。たとえば、左の子のサイズは16で、ルート要素のランクは17(ランク+左の子のサイズ+1)です。
したがって、ランク20の要素を探す必要があります。そのため、当然、その右の子にトラバースする必要があります
右の子にトラバースし、上記の式に基づいて右の子のランクを検索します(上記の式に基づいて、注意:ランクフラグの値は17です)、ランクに基づいて右に行くか左に行くかを決定します
このコードは私の割り当てのものであり、条件の1つは配列を使用しないことでした。コードをよりコンパクトで読みやすくするために、stringName.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);
}
}
`
逆の順序トラバーサルを使用します。つまり、左の子ではなく右の子に最初に移動します。これは次のようにして再帰的に取得できます。
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);
}
}
// 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;
}
最大の要素から最小の要素までツリーを移動し、要求された位置に到達したときに値を返すことで、それを行います。私は2番目に大きい値に対して同様のタスクを実装しました。値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;
}
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)
}
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);
}