mirror of
https://hub.njuu.cf/TheAlgorithms/C-Plus-Plus.git
synced 2023-10-11 13:05:55 +08:00
feat: add inorder successor in bst
This commit is contained in:
parent
394811e601
commit
a527532eea
345
operations_on_datastructures/inorder_successor_of_bst.cpp
Normal file
345
operations_on_datastructures/inorder_successor_of_bst.cpp
Normal file
@ -0,0 +1,345 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief An implementation for finding [Inorder successor of binary search tree](link)
|
||||
* Inorder successor of a node is the next node in Inorder traversal of the Binary Tree. Inorder Successor is NULL for the last node in Inorder traversal.
|
||||
*
|
||||
* ### Case 1 : The given node has right node/subtree
|
||||
* In this case the left most deepest node in the right subtree will come just after the given node as we go to left
|
||||
* deep in inorder.
|
||||
* - Go deep to left most node in right subtree.
|
||||
* OR, we can also say in case if BST, find the minimum of the subtree for a given node.
|
||||
*
|
||||
* ### Case 2 : The given node does not have a right node/subtree
|
||||
*
|
||||
* #### Method 1 : Use parent pointer (store the address of parent nodes)
|
||||
* If a node does not have right subtree, and we already visited the node itself,
|
||||
* then the next node will be its parent node according to inorder traversal, and if we are going to parent from left,
|
||||
* then the parent would be unvisited. In other words, go to the nearest ancestor for which given node would be in left subtree.
|
||||
*
|
||||
* #### Method 2 : Search from the root node
|
||||
* In case if there is no link to the parent, we need to walk the tree starting from the root node to the given node,
|
||||
* by doing so, we are visiting every ancestor of the given node. In order successor would be the deepest node in
|
||||
* this path for which given node is in left subtree.
|
||||
*
|
||||
* @author [Nitin Sharma](https://github.com/foo290)
|
||||
* */
|
||||
|
||||
|
||||
#include <iostream> /// For IO Operations
|
||||
#include <vector> /// For std::vector
|
||||
#include <cassert> /// For assert
|
||||
|
||||
namespace binary_search_tree {
|
||||
/**
|
||||
* @brief A Node structure representing a single node in bst.
|
||||
*/
|
||||
class Node {
|
||||
public:
|
||||
int64_t data; ///< The key/value of the node
|
||||
Node *left; ///< Pointer to Left child
|
||||
Node *right; ///< Pointer to right child
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allocates a new node in heap for given data and returns it's pointer.
|
||||
* @param data Data for the node.
|
||||
* @returns A pointer to the newly allocated Node.
|
||||
* */
|
||||
Node *makeNode(int64_t data) {
|
||||
Node *node = new Node();
|
||||
node->data = data; ///< setting data for node
|
||||
node->left = nullptr; ///< setting left child as null
|
||||
node->right = nullptr; ///< setting right child as null
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts the given data in BST while maintaining the properties of BST.
|
||||
* @param root Pointer to the root node of the BST
|
||||
* @param data Data to be inserted.
|
||||
* @returns Node* Pointer to the root node.
|
||||
* */
|
||||
Node *Insert(Node *root, int64_t data) {
|
||||
if (root == nullptr) {
|
||||
root = makeNode(data);
|
||||
} else if (data <= root->data) {
|
||||
root->left = Insert(root->left, data);
|
||||
} else {
|
||||
root->right = Insert(root->right, data);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Searches the given data in BST and returns the pointer to the node containing that data.
|
||||
* @param root Pointer to the root node of the BST
|
||||
* @param data Data to be Searched.
|
||||
* @returns Node* pointer to the found node
|
||||
* */
|
||||
Node *getNode(Node *root, int64_t data) {
|
||||
if (root == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
else if (root->data == data) {
|
||||
return root;
|
||||
}
|
||||
else if (data > root->data) {
|
||||
/// recursive call
|
||||
return getNode(root->right, data);
|
||||
}
|
||||
else {
|
||||
/// recursive call
|
||||
return getNode(root->left, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds and return the minimum node in BST.
|
||||
* @param root A pointer to root node.
|
||||
* @returns Node* Pointer to the found node
|
||||
* */
|
||||
Node *findMinNode(Node *root) {
|
||||
if (root == nullptr) {
|
||||
return root;
|
||||
}
|
||||
while (root->left != nullptr) {
|
||||
root = root->left;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Search from the root node as we need to walk the tree starting from the root node to the given node,
|
||||
* by doing so, we are visiting every ancestor of the given node.
|
||||
* In order successor would be the deepest node in this path for which given node is in left subtree.
|
||||
*
|
||||
* @param root A pointer to the root node of the BST
|
||||
* @param data The data (or the data of node) for which we have to find inorder successor.
|
||||
* @returns Node pointer to the inorder successor node.
|
||||
* */
|
||||
Node *getInorderSuccessor(Node *root, int64_t data) {
|
||||
// O(h)
|
||||
|
||||
Node *current = getNode(root, data);
|
||||
if (current == nullptr) return nullptr;
|
||||
|
||||
// Case - 1
|
||||
if (current->right != nullptr) {
|
||||
return findMinNode(current->right);
|
||||
}
|
||||
// case - 2
|
||||
else {
|
||||
Node *successor = nullptr;
|
||||
Node *ancestor = root;
|
||||
|
||||
while (ancestor != current) {
|
||||
|
||||
if (current->data < ancestor->data) {
|
||||
// This means my current node is in left of the root node
|
||||
successor = ancestor;
|
||||
ancestor = ancestor->left; // keep going left
|
||||
} else {
|
||||
ancestor = ancestor->right;
|
||||
}
|
||||
}
|
||||
return successor; // Nodes with maximum vales will not have a successor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints the BST in inorder traversal using recursion.
|
||||
* @param root A pointer to the root node of the BST.
|
||||
* @returns void
|
||||
* */
|
||||
void printInorder(Node *root) {
|
||||
if (root == nullptr) return;
|
||||
|
||||
printInorder(root->left); /// recursive call to left subtree
|
||||
std::cout << root->data << " ";
|
||||
printInorder(root->right); /// recursive call to right subtree
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function is used in test cases to quickly create BST containing large data instead of hard coding it in code.
|
||||
* For a given root, this will add all the nodes containing data passes in data vector.
|
||||
* @param root Pointer to the root node.
|
||||
* @param data A vector containing integer values which are suppose to be inserted as nodes in BST.
|
||||
* @returns Node pointer to the root node.
|
||||
* */
|
||||
Node *makeBST(Node *root, const std::vector<int64_t> &data) {
|
||||
for (int64_t values: data) {
|
||||
root = Insert(root, values);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
} // namespace binary_search_tree
|
||||
|
||||
/**
|
||||
* @brief class encapsulating the necessary test cases
|
||||
*/
|
||||
class TestCases {
|
||||
private:
|
||||
/**
|
||||
* @brief A function to print given message on console.
|
||||
* @tparam T Type of the given message.
|
||||
* @returns void
|
||||
* */
|
||||
template <typename T>
|
||||
void log(T msg) {
|
||||
// It's just to avoid writing cout and endl
|
||||
std::cout << "[TESTS] : ---> " << msg << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Executes test cases
|
||||
* @returns void
|
||||
* */
|
||||
void runTests() {
|
||||
log("Running Tests...");
|
||||
|
||||
testCase_1();
|
||||
testCase_2();
|
||||
testCase_3();
|
||||
|
||||
log("Test Cases over!");
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A test case contains edge case, printing inorder successor of last node.
|
||||
* @returns void
|
||||
* */
|
||||
void testCase_1() {
|
||||
const binary_search_tree::Node *expectedOutput = nullptr; ///< Expected output of this test
|
||||
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
log("This is test case 1 : ");
|
||||
log("Description:");
|
||||
log(" EDGE CASE : Printing inorder successor for last node in the BST, Output will be nullptr.");
|
||||
|
||||
binary_search_tree::Node *root = nullptr;
|
||||
std::vector<int64_t> node_data {20, 3, 5, 6, 2, 23, 45 , 78, 21}; ///< Data to make nodes in BST
|
||||
|
||||
root = binary_search_tree::makeBST(root, node_data); ///< Adding nodes to BST
|
||||
|
||||
std::cout<<"Inorder sequence is : ";
|
||||
binary_search_tree::printInorder(root); ///< Printing inorder to cross-verify.
|
||||
std::cout<<std::endl;
|
||||
|
||||
binary_search_tree::Node *inorderSuccessor =
|
||||
binary_search_tree::getInorderSuccessor(root, 78); ///< The inorder successor node for given data
|
||||
|
||||
log("Checking assert expression...");
|
||||
assert(inorderSuccessor == expectedOutput);
|
||||
log("Assertion check passed!");
|
||||
|
||||
log("[PASS] : TEST CASE 1 PASS!");
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A test case which contains main list of 100 elements and sublist
|
||||
* of 20.
|
||||
* @returns void
|
||||
* */
|
||||
void testCase_2() {
|
||||
const int expectedOutput = 21; ///< Expected output of this test
|
||||
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
log("This is test case 2 : ");
|
||||
|
||||
binary_search_tree::Node *root = nullptr;
|
||||
std::vector<int64_t> node_data {20, 3, 5, 6, 2, 23, 45 , 78, 21}; ///< Data to make nodes in BST
|
||||
|
||||
root = binary_search_tree::makeBST(root, node_data); ///< Adding nodes to BST
|
||||
|
||||
std::cout<<"Inorder sequence is : ";
|
||||
binary_search_tree::printInorder(root); ///< Printing inorder to cross-verify.
|
||||
std::cout<<std::endl;
|
||||
|
||||
binary_search_tree::Node *inorderSuccessor =
|
||||
binary_search_tree::getInorderSuccessor(root, 20); ///< The inorder successor node for given data
|
||||
|
||||
log("Checking assert expression...");
|
||||
assert(inorderSuccessor->data == expectedOutput);
|
||||
log("Assertion check passed!");
|
||||
|
||||
log("[PASS] : TEST CASE 2 PASS!");
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A test case which contains main list of 50 elements and sublist
|
||||
* of 20.
|
||||
* @returns void
|
||||
* */
|
||||
void testCase_3() {
|
||||
const int expectedOutput = 110; ///< Expected output of this test
|
||||
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
log("This is test case 3 : ");
|
||||
|
||||
binary_search_tree::Node *root = nullptr;
|
||||
std::vector<int64_t> node_data {89, 67, 32, 56, 90, 123, 120, 110, 115, 6, 78, 7, 10}; ///< Data to make nodes in BST
|
||||
|
||||
root = binary_search_tree::makeBST(root, node_data); ///< Adding nodes to BST
|
||||
|
||||
std::cout<<"Inorder sequence is : ";
|
||||
binary_search_tree::printInorder(root); ///< Printing inorder to cross-verify.
|
||||
std::cout<<std::endl;
|
||||
|
||||
binary_search_tree::Node *inorderSuccessor =
|
||||
binary_search_tree::getInorderSuccessor(root, 90); ///< The inorder successor node for given data
|
||||
|
||||
log("Checking assert expression...");
|
||||
assert(inorderSuccessor->data == expectedOutput);
|
||||
log("Assertion check passed!");
|
||||
|
||||
log("[PASS] : TEST CASE 3 PASS!");
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
TestCases tc;
|
||||
tc.runTests();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @param argc commandline argument count (ignored)
|
||||
* @param argv commandline array of arguments (ignored)
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
test(); /// run self-test implementations
|
||||
|
||||
binary_search_tree::Node *root = nullptr; ///< root node of the bst
|
||||
std::vector<int64_t> node_data{3, 4, 5, 89, 1, 2}; ///< Data to add nodes in BST
|
||||
|
||||
int64_t targetElement = 4; ///< An element to find inorder successor for.
|
||||
root = binary_search_tree::makeBST(root, node_data); ///< Making BST
|
||||
|
||||
binary_search_tree::Node *inorderSuccessor =
|
||||
binary_search_tree::getInorderSuccessor(root, targetElement);
|
||||
|
||||
std::cout<<"In-order sequence is : ";
|
||||
binary_search_tree::printInorder(root);
|
||||
std::cout<<std::endl;
|
||||
|
||||
if (inorderSuccessor == nullptr) {
|
||||
std::cout<<"Inorder successor for last node is NULL"<<std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout<<"Target element is : "<<targetElement<<std::endl;
|
||||
std::cout<<"Inorder successor for target element is : "<<inorderSuccessor->data;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user