📌  相关文章
📜  BST中第K个最大的元素,使用恒定的额外空间

📅  最后修改于: 2021-05-25 00:22:03             🧑  作者: Mango

给定二进制搜索树,任务是在二进制搜索树中找到第K个最大元素。
例子:

Input :  k = 3
         Root of following BST
            10
          /    \
         4      20
        /      /   \
       2     15     40
Output : 15

这个想法是使用基于线程二叉树的反向Morris遍历。线程二叉树使用NULL指针存储后继和前任信息,这有助于我们利用这些NULL指针浪费的内存。

Morris遍历的特殊之处在于我们可以在不使用堆栈或递归的情况下进行有序遍历,从而节省了堆栈或递归调用堆栈所消耗的内存。

反向Morris遍历只是Morris遍历的逆向,它主要用于进行常数为O(1)的反向有序遍历,因此消耗了额外的内存,因为它不使用任何堆栈或递归。

要在二元搜索树中找到第K个最大元素,最简单的逻辑是进行反向有序遍历,而进行反向有序遍历只需保留访问的节点数即可。当计数等于k时,我们停止遍历并打印数据。它利用以下事实:反向有序遍历将为我们提供以降序排列的列表。

算法

1) Initialize Current as root.
2) Initialize a count variable to 0.
3) While current is not NULL :
   3.1) If current has no right child
   a) Increment count and check if count is equal to K.
      1) If count is equal to K, simply return current 
         Node as it is the Kth largest Node.
   b) Otherwise, Move to the left child of current.

   3.2) Else, here we have 2 cases:
   a) Find the inorder successor of current Node. 
      Inorder successor is the left most Node 
      in the right subtree or right child itself.
   b) If the left child of the inorder successor is NULL:
      1) Set current as the left child of its inorder 
         successor.
      2) Move current Node to its right.
   c) Else, if the threaded link between the current Node 
      and it's inorder successor already exists :
      1) Set left pointer of the inorder successor as NULL.
      2) Increment count and check if count is equal to K.
           a) If count is equal to K, simply return current
              Node as it is the Kth largest Node.

      3) Otherwise, Move current to it's left child.
C++
// CPP code for finding K-th largest Node using O(1)
// extra memory and reverse Morris traversal.
#include 
using namespace std;
  
struct Node {
    int data;
    struct Node *left, *right;
};
  
// helper function to create a new Node
Node* newNode(int data)
{
    Node* temp = new Node;
    temp->data = data;
    temp->right = temp->left = NULL;
    return temp;
}
  
Node* KthLargestUsingMorrisTraversal(Node* root, int k)
{
    Node* curr = root;
    Node* Klargest = NULL;
  
    // count variable to keep count of visited Nodes
    int count = 0;
  
    while (curr != NULL) {
        // if right child is NULL
        if (curr->right == NULL) {
  
            // first increment count and check if count = k
            if (++count == k)
                Klargest = curr;
  
            // otherwise move to the left child
            curr = curr->left;
        }
  
        else {
  
            // find inorder successor of current Node
            Node* succ = curr->right;
  
            while (succ->left != NULL && succ->left != curr)
                succ = succ->left;
  
            if (succ->left == NULL) {
  
                // set left child of successor to the
                // current Node
                succ->left = curr;
  
                // move current to its right
                curr = curr->right;
            }
  
            // restoring the tree back to original binary
            //  search tree removing threaded links
            else {
  
                succ->left = NULL;
  
                if (++count == k)
                    Klargest = curr;
  
                // move current to its left child
                curr = curr->left;
            }
        }
    }
  
    return Klargest;
}
  
int main()
{
    // Your C++ Code
    /* Constructed binary tree is
          4
        /   \
       2     7
     /  \   /  \
    1    3  6    10 */
  
    Node* root = newNode(4);
    root->left = newNode(2);
    root->right = newNode(7);
    root->left->left = newNode(1);
    root->left->right = newNode(3);
    root->right->left = newNode(6);
    root->right->right = newNode(10);
  
    cout << "Finding K-th largest Node in BST : "
         << KthLargestUsingMorrisTraversal(root, 2)->data;
  
    return 0;
}


Java
// Java Program for finding K-th largest Node using O(1) 
// extra memory and reverse Morris traversal. 
class GfG 
{ 
  
static class Node 
{ 
    int data; 
    Node left, right; 
}
  
// helper function to create a new Node 
static Node newNode(int data) 
{ 
    Node temp = new Node(); 
    temp.data = data; 
    temp.right = null;
    temp.left = null; 
    return temp; 
} 
  
static Node KthLargestUsingMorrisTraversal(Node root, int k) 
{ 
    Node curr = root; 
    Node Klargest = null; 
  
    // count variable to keep count of visited Nodes 
    int count = 0; 
  
    while (curr != null)
    { 
        // if right child is NULL 
        if (curr.right == null) 
        { 
  
            // first increment count and check if count = k 
            if (++count == k) 
                Klargest = curr; 
  
            // otherwise move to the left child 
            curr = curr.left; 
        } 
  
        else 
        { 
  
            // find inorder successor of current Node 
            Node succ = curr.right; 
  
            while (succ.left != null && succ.left != curr) 
                succ = succ.left; 
  
            if (succ.left == null)
            { 
  
                // set left child of successor to the 
                // current Node 
                succ.left = curr; 
  
                // move current to its right 
                curr = curr.right; 
            } 
  
            // restoring the tree back to original binary 
            // search tree removing threaded links 
            else
            { 
  
                succ.left = null; 
  
                if (++count == k) 
                    Klargest = curr; 
  
                // move current to its left child 
                curr = curr.left; 
            } 
        } 
    } 
    return Klargest; 
} 
  
// Driver code
public static void main(String[] args) 
{ 
    // Your Java Code 
    /* Constructed binary tree is 
        4 
        / \ 
    2 7 
    / \ / \ 
    1 3 6 10 */
  
    Node root = newNode(4); 
    root.left = newNode(2); 
    root.right = newNode(7); 
    root.left.left = newNode(1); 
    root.left.right = newNode(3); 
    root.right.left = newNode(6); 
    root.right.right = newNode(10); 
  
    System.out.println("Finding K-th largest Node in BST : " + 
                    KthLargestUsingMorrisTraversal(root, 2).data); 
} 
}


Python3
# Python3 code for finding K-th largest 
# Node using O(1) extra memory and 
# reverse Morris traversal. 
  
# helper function to create a new Node 
class newNode:
    def __init__(self, data):
        self.data = data 
        self.right = self.left = None
  
def KthLargestUsingMorrisTraversal(root, k):
    curr = root 
    Klargest = None
  
    # count variable to keep count 
    # of visited Nodes 
    count = 0
  
    while (curr != None):
          
        # if right child is None 
        if (curr.right == None): 
  
            # first increment count and
            # check if count = k
            count += 1
            if (count == k): 
                Klargest = curr 
  
            # otherwise move to the left child 
            curr = curr.left
  
        else: 
  
            # find inorder successor of 
            # current Node 
            succ = curr.right 
  
            while (succ.left != None and 
                   succ.left != curr): 
                succ = succ.left 
  
            if (succ.left == None): 
  
                # set left child of successor 
                # to the current Node 
                succ.left = curr 
  
                # move current to its right 
                curr = curr.right
  
            # restoring the tree back to  
            # original binary search tree
            # removing threaded links 
            else:
  
                succ.left = None
                count += 1
                if (count == k): 
                    Klargest = curr 
  
                # move current to its left child 
                curr = curr.left
  
    return Klargest
  
# Driver Code
if __name__ == '__main__':
  
    # Constructed binary tree is 
    #     4 
    #     / \ 
    # 2     7 
    # / \ / \ 
    # 1 3 6 10 
    root = newNode(4) 
    root.left = newNode(2) 
    root.right = newNode(7) 
    root.left.left = newNode(1) 
    root.left.right = newNode(3) 
    root.right.left = newNode(6) 
    root.right.right = newNode(10) 
  
    print("Finding K-th largest Node in BST : ", 
           KthLargestUsingMorrisTraversal(root, 2).data)
  
# This code is contributed by PranchalK


C#
// C# Program for finding K-th largest Node using O(1) 
// extra memory and reverse Morris traversal. 
using System;
using System.Collections.Generic;
  
class GfG 
{ 
  
public class Node 
{ 
    public int data; 
    public Node left, right; 
}
  
// helper function to create a new Node 
static Node newNode(int data) 
{ 
    Node temp = new Node(); 
    temp.data = data; 
    temp.right = null;
    temp.left = null; 
    return temp; 
} 
  
static Node KthLargestUsingMorrisTraversal(Node root, int k) 
{ 
    Node curr = root; 
    Node Klargest = null; 
  
    // count variable to keep count of visited Nodes 
    int count = 0; 
  
    while (curr != null)
    { 
        // if right child is NULL 
        if (curr.right == null) 
        { 
  
            // first increment count and check if count = k 
            if (++count == k) 
                Klargest = curr; 
  
            // otherwise move to the left child 
            curr = curr.left; 
        } 
  
        else
        { 
  
            // find inorder successor of current Node 
            Node succ = curr.right; 
  
            while (succ.left != null && succ.left != curr) 
                succ = succ.left; 
  
            if (succ.left == null)
            { 
  
                // set left child of successor to the 
                // current Node 
                succ.left = curr; 
  
                // move current to its right 
                curr = curr.right; 
            } 
  
            // restoring the tree back to original binary 
            // search tree removing threaded links 
            else
            { 
  
                succ.left = null; 
  
                if (++count == k) 
                    Klargest = curr; 
  
                // move current to its left child 
                curr = curr.left; 
            } 
        } 
    } 
    return Klargest; 
} 
  
// Driver code
public static void Main(String[] args) 
{ 
    // Your C# Code 
    /* Constructed binary tree is 
        4 
        / \ 
    2 7 
    / \ / \ 
    1 3 6 10 */
  
    Node root = newNode(4); 
    root.left = newNode(2); 
    root.right = newNode(7); 
    root.left.left = newNode(1); 
    root.left.right = newNode(3); 
    root.right.left = newNode(6); 
    root.right.right = newNode(10); 
  
    Console.Write("Finding K-th largest Node in BST : " + 
                    KthLargestUsingMorrisTraversal(root, 2).data); 
} 
}
  
// This code has been contributed by 29AjayKumar


输出:
Finding K-th largest Node in BST : 7

时间复杂度: O(n)
辅助空间: O(1)