需求:
输入: 2, 3, 1, 4, 5. 生成一颗二叉搜索树应: 2 1 3 x x x 4 x x x x x x x 5 (x代表该节点为空)
输入指定删除位置, 并展示删除后的二叉搜索树.
如: 删除5, 该位置无节点, 弹出节点为空的信息. 例如删除6, 则删除后的二叉搜索树为: 2 1 3 x x x 5
每次删除后, 优先找其左子树的最大值进行替换, 若无左子树则找其右子树的最小值进行替换.
这里只给出递归版本, 不给出迭代版本
Program environment:
Operation System: Ubuntu 14.04
Ide: Eclipse
Compiler: g++
Language: c++
Requirements:
Deletion from the binary search tree should be O(1) in space complexity. Two versions: one is iterative version, the other is recursive version.
Ideas:
To achieve the O(1) space complexity, it’s natural to coming up with the array’s implementation.
Facing with this problem, the first question is the length of the array as the infinite input not until -1 entered. Because is a binary tree, but not a AVL tree, the worst case occurs when input is like: 1, 2, 3, 4, 5... in increasing order which will create a right skew binary search tree. If the input has n nodes, then the array length should be 2^n.
From the properties of binary search tree, we know there is no same element in the tree. But requirement doesn’t state clearly that all nodes are different, so there’s a trap here.
The built binary search tree must meet the property that any node must be greater than any node of its left subtree and smaller than any node of its right subtree. Following this rule, select the first node as tree’s root, for each node, then do the comparison until a no occupied index found.
Deletion operation is only O(1). But before the deletion we should find the node(denoted as d) which is ready to take place of the deleting node. The finding operation is O(h), h is the height of the tree. The finding is also based on the comparison obtaining the rule: priority is finding the largest node of d’s left subtree, if d has no left subtree then find the smallest node of d’s right subtree, else d is the leaf node whick can be directly deleted. The finding function has two version, one is iterative version the other is recursive version.
Rebuilding d’s subtrees is the hardest part of this assignment, i think. To implement the rebuilding, i choose a queue to store the d’s descendants. While queue is used for level traversal. The procedure is shown below:
We first replace the red node which client asks to be deleted with the green. Then we move d’s subtree upward level by level.
Input & Output:
Input: a. a list of nodes. (-1 to terminate)
b. the index of executing deletion.
Output: a. the binary search tree
b. the binary search tree after each deletion
//============================================================================
// Name : recursive_version.cpp
// Author : Reid Chan
// Version :
// Copyright : Your copyright notice
//============================================================================
#include <iostream>
#include <queue>
#include <cmath>
using namespace std;
#define null -1 // Used for representing the null tree node
int end = -1; // Used for recording the index of the last node
int size; // Used for recording the number of nodes in the tree
/**
* Used to build the binary search tree.
* t: the tree array
* q: the input nodes stored in a queue.
*/
void buildTree(int *t, queue<int> q) {
t[1] = q.front(); // the first node will be root.
q.pop();
int child, parent;
while (!q.empty()) {
/* get the node and locate it at the appropriate index */
child = q.front();
parent = 1;
while (t[parent] != null) {
if (child > t[parent]) {
parent = 2 * parent + 1;
} else if (child < t[parent]) {
parent = 2 * parent;
} else {
/**
* if there're nodes that have the same element, only the first will be kept
* the number of the nodes should minus one every time occurs
*/
size--;
break;
}
}
t[parent] = child;
if (parent > end) { end = parent; } // record the index of last node
q.pop();
}
}
/**
* it's invoked after deletion completed.
* it's for placing the nodes to the appropriate index respectively.
* a queue is used here, it's for level traversal a technique applied when rebuilding
*/
void rebuild(int *t, int i, int flag)
{
queue<int> que;
if (i * 2 <= end && t[i * 2] != null) { que.push(i * 2); }
if (i * 2 + 1 <= end && t[i * 2 + 1] != null) { que.push(i * 2 + 1); }
t[i] = -1;
int x;
while (!que.empty()) {
// get the current node's index
x = que.front();
// check if the current node has children,
// push the children in the queue if exists.
if (t[x * 2] != null && x * 2 <= end) { que.push(x * 2); }
if (t[x * 2 + 1] != null && x * 2 + 1 <= end) { que.push(x * 2 + 1); }
// replace the parent node with the current node.
if (flag == 1) { t[(x + 1) / 2] = t[x]; }
else { t[(x - 1) / 2] = t[x]; }
// set the current node as null, and move to the next node
t[x] = null;
que.pop();
}
}
/**
* function for keeping track of the last node of the tree
*/
void getEnd(int *t)
{
for (int i = end; i >= 1; i--) {
if (t[i] != null) {
end = i;
break;
}
}
}
void recursiveFind(int *t, int &i, int f) {
if (t[i] == null) {
i = (f ? (i - 1) / 2 : i / 2);
return;
}
if (f == 1) {
recursiveFind(t, (i = i * 2 + 1), 1);
} else {
recursiveFind(t, (i = i * 2), 0);
}
}
/**
* delete the node at index i, then rebuild its subtrees.
*/
void deleteNode(int *t, int i) {
int flag;
int p = i;
/********* recursive version *********/
/**
* 1. if left subtree is exist. find the largest node of the left subtree,
* then replace the deleted node with it.
*/
if (t[p * 2] != null) {
p *= 2;
flag = 1;
recursiveFind(t, p, flag);
}
/**
* 2. if left subtree is empty, and right subtree is exist.
* find the smallest node of the right subtree,
* then replace the deleted node with it.
*/
else if (t[p * 2 + 1] != null) {
p = p * 2 + 1;
flag = 0;
recursiveFind(t, p, flag);
}
t[i] = t[p]; // replacement operation
rebuild(t, p, flag); // rebuild its subtree.
getEnd(t);
}
/**
* printOut the whole tree
*/
void printOut(int *t) {
cout << "Binary search tree is:" << endl;
for (int i = 1; i <= end; ++i) {
if (t[i] == null) {
cout << 'x' << ' ';
continue;
}
cout << t[i] << ' ';
}
cout << endl;
}
int main() {
cout << "Please input(-1 as an end): ";
queue<int> nodes; // used to store input nodes. FIFO
int num;
while (cin >> num && num != null) {
nodes.push(num);
}
size = nodes.size(); // get the number of nodes(notice that possibly some will be repeated)
int sz = pow(2, size); // the worst case is right-skewed tree, thus at least require 2^n space.
int *tree = new int[sz]; // initialization the tree nodes as -1(null)
for (int i = 0; i < sz; ++i) {
tree[i] = -1;
}
buildTree(tree, nodes);
printOut(tree);
int del;
cout << "Delete element at index(-1 to terminate deletion): ";
/* to user the index begins at 0, but in implementation it begins at 1
* that's why a plus one occurs
*/
while (cin >> del && del != -1) {
if (size == 0) {
cout << "Error: The tree is empty~" << endl;
return 0;
}
if (del + 1> end) {
cout << "Error: It's out of range!" << endl;
} else if (tree[del + 1] == null) {
cout << "Warning: no element at index " << del << "."<< endl;
} else {
deleteNode(tree, del + 1);
size--;
printOut(tree);
}
cout << "Delete element at index(-1 to terminate deletion): ";
}
return 0;
}