これは面接の質問です。 BSTで2番目の最大値を見つけます。
Max要素は、BSTの右端のリーフです。 2番目の最大値は、その親またはその左の子のいずれかです。したがって、解決策は、BSTをトラバースして右端の葉を見つけ、その親と左の子を確認することです。
それは意味がありますか?
正しいサブツリーを探索する場所で変更された inorder traversal を実行することにより、BSTのノードを逆の順序でリストできることを思い出してください。最初。これは単純なアルゴリズムにつながります:
Node rightmost = findRightmostNode(root)
if (rightmost.left != null) {
return findRightmostNode(rightmost.left)
else{
return rightmost.parent
}
ツリーに要素が1つしかない場合は、nullを返します。
いいえ、それは間違っています。このBSTを検討してください。
137
/
42
\
99
ここで、最大値から2番目の値は、最大値の左の子の右端の子です。最大値の親、または最大値の左の子の右端のサブチャイルドをチェックするように、アルゴリズムを更新する必要があります。
また、maxは必ずしも右端のノードではないことに注意してくださいleafノード、それはツリーの右の背骨の下部にあるノードです。上記では、137は葉ではありません。
お役に立てれば!
public static int findSecondLargestValueInBST(Node root)
{
int secondMax;
Node pre = root;
Node cur = root;
while (cur.Right != null)
{
pre = cur;
cur = cur.Right;
}
if (cur.Left != null)
{
cur = cur.Left;
while (cur.Right != null)
cur = cur.Right;
secondMax = cur.Value;
}
else
{
if (cur == root && pre == root)
//Only one node in BST
secondMax = int.MinValue;
else
secondMax = pre.Value;
}
return secondMax;
}
アルゴは次のようになります
1. find the largest number in the tree.
private static int findLargestValueInTree(Node root) {
while (root.right != null) {
root = root.right;
}
return root.data;
}
2. Find the largest number in the tree that is smaller than the number we found in step 1
public static int findSecondLargest(Node root, int largest, int current) {
while (root != null) {
if (root.data < largest) {
current = root.data;
root = root.right;
} else {
root = root.left;
}
}
return current;
}
'current'は、ステップ1で見つかった数よりも小さい現在の最大数を追跡します
時間計算量O(logN)および空間計算量O(1)を使用したはるかに簡単な反復アプローチ
public static void main(String[] args) {
BinaryTreeNode result=isBinarySearchTree.secondLargest(rootNode);
System.out.println(result.data);
}
private BinaryTreeNode secondLargest(BinaryTreeNode node) {
BinaryTreeNode prevNode=null; //2nd largest Element
BinaryTreeNode currNode=node;
if(null == currNode)
return prevNode;
while(currNode.right != null){
prevNode=currNode;
currNode=currNode.right;
}
if(currNode.left != null){
currNode=currNode.left;
while(currNode.right != null){
currNode=currNode.right;
}
prevNode=currNode;
}
return prevNode;
}
1つのトラバーサルバリアント:
public Tree GetSecondMax(Tree root)
{
Tree parentOfMax = null;
var maxNode = GetMaxNode(root, ref parentOfMax);
if (maxNode == root || maxnode.left != null)
{
// if maximum node is root or have left subtree, then return maximum from left subtree
return GetMaxNode(maxnode.left, ref parentOfMax);
}
// if maximum node is not root, then return parent of maximum node
return parentOfMax;
}
private Tree GetMaxNode(Tree root, ref Tree previousNode)
{
if (root == null || root.right == null)
{
// The most right element have reached
return root;
}
// we was there
previousNode = root;
return GetMaxNode(root.right, ref previousNode);
}
int getmax(node *root)
{
if(root->right == NULL)
{
return root->d;
}
return getmax(root->right);
}
int secondmax(node *root)
{
if(root == NULL)
{
return -1;
}
if(root->right == NULL && root->left != NULL)
{
return getmax(root->left);
}
if(root->right != NULL)
{
if(root->right->right == NULL && root->right->left == NULL)
{
return root->d;
}
}
return secondmax(root->right);
}
ツリーを最大要素から最小要素に移動し、要求された位置に到達したときに値を返すことによってそれを行います。 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;
}
単純なJavaScriptの実装。
function Node (value, left, right) {
this.value = value;
this.left = left;
this.right = right;
}
function second (node, prev, wentLeft) {
if (node.right) {
return second(node.right, node, wentLeft);
} else if (node.left) {
return second(node.left, node, true);
} else {
if (wentLeft) return node.value;
return prev.value;
}
}
second(root);
これについて非常に直感的に考える方法は、次の2つのケースを検討することです。 2番目に大きいNodeをS、最大のノードをLとします。
i)SはLより「早い」BSTに挿入されます。ii)SはLより「遅い」BSTに挿入されます。
最初のケースでは、LがSの右の子であることは明らかです。これは、L以外のノードがSより小さいため、Sの右側に配置されないためです。したがって、Lが配置されると、 Sの正しい子になります。
2番目のケースでは、Sが挿入されるまでに、LがBSTの右端のノードになります。明らかに、Lは最大であるため、適切な子はありません。ただし、Lは独自の左サブツリーを持つことができます。 Sが挿入されると、SはLに出会うまで「右のパス」をたどり、次に左に曲がってLの左のサブツリーに移動します。ここで、Lの左のサブツリーのすべてのノードがSよりも小さいことがわかっているため、Sサブツリーの右端のノードになります。
int getSecondLargest(Node root){
if(root==null)
return 0;
Node curr=root;
Node prev=root;
//Go to the largest node
while(curr.right != null){
prev = curr;
curr= curr.right;
}
//If largest Node has left child, Then largest element of tree with its root as largest.left will be the second largest number.
if(curr.left == null)
return prev.data;
else
return findLargest(curr.left);
}
int findLargest(Node root){
// No need toi check for null condition as in getSecondLargest method, its already done.
Node curr=root;
//To find largest just keep on going to right child till leaf is encountered.
while(curr.right != null){
curr = curr.right;
}
return curr.data;
}
あなたは正しい答えに近づいています。
これが直感的な答えへの私の試みです。
最大のノードは右端のノードです。
右端のノードの左のサブツリーの下にあるものはすべて、右端のノードを除くすべての要素よりも大きくなります。したがって、このサブツリーの最大のノードが答えです。
左のサブツリーがない場合、右端のノードの親は、右端のノードを除く他のすべてのノードよりも大きいものです。
アイデアは、右側に何もなくなるまで、右端まで移動することです。左にある場合は、それを取り、右端まで進みます。左に曲がった場合、答えは最後に遭遇したノードです。それ以外の場合、答えは最後から2番目に遭遇したノードです。
Javaの再帰的ソリューションは次のとおりです。
public TreeNode getSecondLargest(TreeNode root) {
if(root == null || (root.left == null && root.right == null))
throw new IllegalArgumentException("The tree must have at least two nodes");
return helper(root, null, false);
}
private TreeNode helper(TreeNode root, TreeNode parent, boolean wentLeft) {
if(root.right != null) return helper(root.right, root, wentLeft);
if(root.left != null && !wentLeft) return helper(root.left, root, true);
if(wentLeft) return root;
else return parent;
}
時間計算量はO(lg n)です。
このアルゴリズムは、ツリーで1回実行し、Item1
で最大のアイテムを返し、Item2
で2番目に大きいアイテムを返します。ソート呼び出しは、ツリーサイズに依存しないため、O(1)です。したがって、合計時間計算量はO(N)であり、空間計算量はO(log(N))の場合木はバランスが取れています。
public static Tuple<int, int> SecondLargest(TreeNode<int> node)
{
int thisValue = node.Value;
if ((node.Left == null || node.Left.Right == null) && node.Right == null)
{
return new Tuple<int, int>(thisValue, -int.MaxValue);
}
else if (node.Left == null || node.Left.Right == null)
{
Tuple<int, int> right = SecondLargest(node.Right);
List<int> sortLargest = new List<int>(new int[] { right.Item1, right.Item2, thisValue });
sortLargest.Sort();
return new Tuple<int, int>(sortLargest[2], sortLargest[1]);
}
else if (node.Right == null)
{
Tuple<int, int> left = SecondLargest(node.Left.Right);
List<int> sortLargest = new List<int>(new int[] { left.Item1, left.Item2, thisValue });
sortLargest.Sort();
return new Tuple<int, int>(sortLargest[2], sortLargest[1]);
}
else
{
Tuple<int, int> left = SecondLargest(node.Left.Right);
Tuple<int, int> right = SecondLargest(node.Right);
List<int> sortLargest = new List<int>(new int[] { left.Item1, left.Item2, right.Item1, right.Item2, thisValue });
sortLargest.Sort();
return new Tuple<int, int>(sortLargest[4], sortLargest[3]);
}
}