查找樹ADT——查找二叉樹
定義:對於樹中的每個節點X,它的左子樹中的所有項的值小於X中的項,而它的右子樹中所有項的值大於X中的項。
現在給出字段和方法定義(BinarySearchTree.h)
#include <queue> class BinarySearchTree { private: struct Node { int value; Node* left; Node* right; }; Node* root; void insert(Node*, int); void traversal(Node*, std::queue<int>&); bool checkNode(Node*, int); int findRoot(Node*, int, int); public: BinarySearchTree() : root(nullptr) {}; void insert(int); std::queue<int> traversal(); int findRoot(int, int); // 查找兩個節點的公共父節點 std::queue<int> morrisTraversal(); // (Morris方法)不使用遞歸和棧遍歷二叉樹 };
查找二叉樹的遍歷可以采用遍歷和非遍歷兩種算法。
一、添加元素(insert)
現在假設要添加這樣一組整數10,7,2,6,13,11,17,3。按照順序形成的查找二叉樹應該如下圖:

算法實現:
void BinarySearchTree::insert(int val) { Node* node = new Node(); node->value = val; node->left = nullptr; node->right = nullptr; if (root == nullptr) { root = node; } else { insert(root, val); } } void BinarySearchTree::insert(Node* node, int val) { if (val < node->value) { if (node->left != nullptr) { insert(node->left, val); } else { Node* chdNode = new Node(); chdNode->value = val; chdNode->left = nullptr; chdNode->right = nullptr; node->left = chdNode; } } else { if (node->right != nullptr) { insert(node->right, val); } else { Node* chdNode = new Node(); chdNode->value = val; chdNode->left = nullptr; chdNode->right = nullptr; node->right = chdNode; } } }
二、遍歷元素(遞歸)
查找二叉樹的遍歷需要采用中序法。即對於樹中的每個節點來說首先處理左子樹然后處理根節點再處理右子樹。
算法實現:
std::queue<int> BinarySearchTree::traversal() { std::queue<int> q; if (root != nullptr) { traversal(root, q); } return q; } void BinarySearchTree::traversal(Node* node, std::queue<int>& q) { if (node->left != nullptr) { traversal(node->left, q); } q.push(node->value); if (node->right != nullptr) { traversal(node->right, q); } }
三、查找樹中任意兩個節點的最小父節點
查找最小父節點的邏輯是對於給出的任意兩個節點分別處於根節點的左側與右側。若兩個節點均處於根節點左側則向左子樹遞歸遍歷,反之,則向右子樹遞歸遍歷。如11和17,它們的最小父節點是13。一種例外情況是要求查找的兩個節點其中一個是另一個的父節點。如2和3,它們的最小父節點是2。
算法實現:
int BinarySearchTree::findRoot(int a, int b) { if (checkNode(root, a) && checkNode(root, b)) { return findRoot(root, a, b); } } int BinarySearchTree::findRoot(Node* node, int a, int b) { if (a<node->value && b>node->value) { return node->value; } else if (a < node->value && b < node->value) { return findRoot(node->left, a, b); } else if (a > node->value && b > node->value) { return findRoot(node->right, a, b); } else { return a == node->value ? a : b; } }
四、遍歷元素(不使用遞歸和棧)
分析:查找二叉樹的遍歷必須采用中序法,由於不能使用遞歸和棧。就需要使用特殊算法,能夠在處理完X節點的右子樹后向上檢索到Y節點。如圖:

Morris算法的特點就是在處理X節點前,首先找到其最右側的子樹b(b->right == nullptr)。然后建立與Y節點的連接(b->right = Y)。處理完成后再刪除臨時連接恢復二叉樹的原裝(b->right = nullptr)
算法實現:
std::queue<int> BinarySearchTree::morrisTraversal() { std::queue<int> q; if (root == nullptr) { return q; } Node* cur = root; Node* prev = nullptr; while (cur != nullptr) { if (cur->left == nullptr) { q.push(cur->value); cur = cur->right; } else { prev = cur->left; while (prev->right != nullptr && prev->right != cur) { prev = prev->right; } if (prev->right == nullptr) { prev->right = cur; cur = cur->left; } else { prev->right = nullptr; q.push(cur->value); cur = cur->right; } } } return q; }
