实现二叉搜索树的插入、删除、查找、遍历(递归、非递归)
遍历实现可分递归、非递归两种版本。这里重点谈非递归实现。
不额外使用栈的非递归遍历又称为 Morris 遍历。
Morris 遍历,使用无堆栈,O(1) 空间进行二叉树遍历。它的原理很简单,利用所有叶子结点的右指针,指向其后继结点,组成一个环,在第二次遍历到这个结点时,由于其左子树已经遍历完了,则访问该结点。
算法伪代码:
while 没有结束
if 当前节点(cur)没有左后代
打印该节点
访问右后代
else
找到当前节点(cur)左后代最右节点(temp)
if temp 的右后代为空 // 第一次访问 temp
让其指向当前节点(cur)
当前节点(cur)转向左后代
if temp 的右后代点指向当前节点(cur) // 第二次访问 temp 其左子树已经访问完
让其指向空
打印 temp 节点
当前节点(cur)转向右后代
流程图:
下图为每一步迭代的结果(从左至右,从上到下),cur代表当前节点,深色节点表示该节点已输出。
代码实现:
void inorderMorrisTraversal(TreeNode *root) {
TreeNode *cur = root, *prev = NULL;
while (cur != NULL)
{
if (cur->left == NULL)
{
printf("%d ", cur->val);
cur = cur->right;
}
else
{
// find predecessor
prev = cur->left;
while (prev->right != NULL && prev->right != cur)
prev = prev->right;
if (prev->right == NULL)
{
prev->right = cur;
cur = cur->left;
}
else
{
prev->right = NULL;
printf("%d ", cur->val);
cur = cur->right;
}
}
}
}
非递归实现,寻找 val 出现的位置,没有找到则放回 val 的父节点
treeNode * findPos(int val) {
treeNode *ptr = m_root;
while (ptr != NULL) {
if (val < ptr->m_ival) {
if (ptr->left != NULL)
ptr = ptr->left;
else
return ptr;
}
else if (val > ptr->m_ival) {
if (ptr->right != NULL)
ptr = ptr->right;
else
return ptr;
}
else // 树中找到 val 直接返回
return ptr;
}
return ptr;
}
插入的实现类似与查找,其递归实现非常简单,在此我们可以直接调用 findPos
完成非递归的实现。
void insert(int val) {
if (m_root == NULL) {
m_root = new treeNode(val);
return;
}
treeNode * trav = findPos(val);
if (val < trav->m_ival)
trav->left = new treeNode(val);
else if (val > trav->m_ival)
trav->right = new treeNode(val);
// 如果 val == trav->m_ival, 就什么也不做
}
比较复杂的是删除节点,需要分三种情况考虑。
// 递归实现,返回删除节点之后的树
treeNode *deleteRecursive(int val, treeNode *tree) {
treeNode *minPos;
if (tree == NULL)
return NULL;
if (val < tree->m_ival)
tree->left = deleteRecursive(val, tree->left);
else if(val > tree->m_ival)
tree->right = deleteRecursive(val, tree->right);
else { // 已经找到值 val
// 两个孩子节点
if (tree->left != NULL && tree->right != NULL) {
minPos = findMin(tree->right);
tree->m_ival = minPos->m_ival;
tree->right = deleteRecursive(minPos->m_ival, tree->right);
}
else { // 一个孩子节点或没有孩子节点
if (tree->right != NULL)
tree = tree->right;
if (tree->left != NULL)
tree = tree->left;
else {
delete tree;
tree = NULL;
}
}
}
return tree;
}
将所有代码整合起来,并加入测试代码。在 ubuntu 14.04 g++ 4.8.4 下编译测试通过。
完整代码如下:
#include<iostream>
using namespace std;
struct treeNode {
int m_ival;
treeNode *left;
treeNode *right;
treeNode(int val): m_ival(val), left(NULL), right(NULL) {};
};
class SearchTree {
public:
SearchTree() { m_root = NULL; }
// 非递归实现 insert
void insert(int val) {
if (m_root == NULL) {
m_root = new treeNode(val);
return;
}
treeNode * trav = findPos(val);
if (val < trav->m_ival)
trav->left = new treeNode(val);
else if (val > trav->m_ival)
trav->right = new treeNode(val);
// 如果 val == trav->m_ival, 就什么也不做
}
string preTravel() {
string result;
preTravelRecursive(m_root, result);
return result;
}
void preTravelRecursive(treeNode * root, string &result) {
if (root != NULL) {
result += to_string(root->m_ival);
preTravelRecursive(root->left, result);
preTravelRecursive(root->right, result);
}
else
result.push_back('#');
}
string inTravel() {
string result;
inTravelRecursive(m_root, result);
return result;
}
void inTravelRecursive(treeNode *tree, string &result) {
if (tree == NULL)
result += '#';
else {
inTravelRecursive(tree->left, result);
result += to_string(tree->m_ival);
inTravelRecursive(tree->right, result);
}
}
string inTravleMorris() {
treeNode *cur = m_root;
treeNode *twice = NULL;
string result = "";
while (cur != NULL) {
if (cur->left == NULL) {
result += to_string(cur->m_ival);
cur = cur->right;
}
else {
twice = cur->left;
while (twice->right != NULL && twice->right != cur)
twice = twice->right;
if (twice->right == NULL) {
twice->right = cur;
cur = cur->left;
}
else {
twice->right = NULL;
result += to_string(cur->m_ival);
cur = cur->right;
}
}
}
return result;
}
void deleteNode(int val) {
deleteRecursive(val, m_root);
}
// 递归实现,返回删除节点之后的树
treeNode *deleteRecursive(int val, treeNode *tree) {
treeNode *minPos;
if (tree == NULL)
return NULL;
if (val < tree->m_ival)
tree->left = deleteRecursive(val, tree->left);
else if(val > tree->m_ival)
tree->right = deleteRecursive(val, tree->right);
else { // 已经找到值 val
// 两个孩子节点
if (tree->left != NULL && tree->right != NULL) {
minPos = findMin(tree->right);
tree->m_ival = minPos->m_ival;
tree->right = deleteRecursive(minPos->m_ival, tree->right);
}
else { // 一个孩子节点或没有孩子节点
if (tree->right != NULL)
tree = tree->right;
if (tree->left != NULL)
tree = tree->left;
else {
delete tree;
tree = NULL;
}
}
}
return tree;
}
private:
treeNode * m_root;
// 寻找 val 应该出现的地点,没有找到则返回 val 的父结点
treeNode * findPos(int val) {
treeNode *ptr = m_root;
while (ptr != NULL) {
if (val < ptr->m_ival) {
if (ptr->left != NULL)
ptr = ptr->left;
else
return ptr;
}
else if (val > ptr->m_ival) {
if (ptr->right != NULL)
ptr = ptr->right;
else
return ptr;
}
else // 树中找到 val 直接返回
return ptr;
}
return ptr;
}
// 寻找最小值所在地点
treeNode * findMin(treeNode *root) {
if (root != NULL)
while (root->left != NULL)
root = root->left;
return root;
}
};
int main() {
SearchTree bst;
bst.insert(5);
bst.insert(3);
bst.insert(14);
bst.insert(2);
bst.insert(4);
bst.insert(9);
bst.insert(15);
bst.insert(7);
bst.insert(8);
cout << bst.preTravel() << endl;
cout << bst.inTravel() << endl;
cout << bst.inTravleMorris() << endl;
bst.deleteNode(3);
cout << bst.preTravel() << endl;
return 0;
}