二叉排序树(BST)就是对于树中任意节点都具有“左子树所有节点 < 根节点 < 右子树所有节点”的性质。下面用代码简单实现一个保存int型数据的二叉排序树:
#include "pch.h"
#include <iostream>
#include <stack>
#include <queue>
//节点结构体
struct Node
{
int data;
Node* parent;
Node* left;
Node* right;
Node() : data(-1),parent(NULL),left(NULL),right(NULL)
{
}
Node(const Node& node) : data(node.data), parent(NULL), left(NULL), right(NULL)
{
}
Node(int num) : data(num), parent(NULL), left(NULL), right(NULL)
{
}
};
class BSTree
{
private:
Node* root;
void CopyTree(Node* parentP,Node* current, Node* nodeP)
{
if (nodeP != NULL)
{
current = new Node(*nodeP);
current->parent = parentP;
CopyTree(current, current->left, nodeP->left);
CopyTree(current, current->right, nodeP->right);
}
}
void DeleteTree(Node* nodeP)
{
if (nodeP != NULL)
{
DeleteTree(nodeP->left);
DeleteTree(nodeP->right);
delete nodeP;
nodeP = NULL;
}
}
//递归调用的中序遍历内部实现
void LNRshow1(Node *p)
{
if (p == NULL)
{
return;
}
else
{
LNRshow1(p->left);
std::cout << p->data << " ";
LNRshow1(p->right);
}
}
public:
BSTree() : root(NULL)
{}
BSTree(int num)
{
root = new Node(num);
}
//拷贝构造函数
BSTree(const BSTree& tree)
{
if (tree.root != NULL)
{
CopyTree(NULL, root, tree.root);
}
}
//析构函数要遍历所有节点释放资源
~BSTree()
{
DeleteTree(root);
}
//插入数据
void insertData(int data)
{
if (root == NULL)
{
root = new Node(data);
return;
}
Node* p = root;
while (p)
{
if (p->data > data)
{
if (p->left == NULL)
{
Node* newNode = new Node(data);
p->left = newNode;
newNode->parent = p;
break;
}
else
{
p = p->left;
continue;
}
}
else
{
if (p->right == NULL)
{
Node* newNode = new Node(data);
p->right = newNode;
newNode->parent = p;
break;
}
else
{
p = p->right;
continue;
}
}
}
}
//查找数据节点
Node* search(int data)
{
Node* p = root;
while (p)
{
if (p->data == data)
{
return p;
}
else if (p->data > data)
{
p = p->left;
}
else
{
p = p->right;
}
}
return p;
}
//删除节点
bool DeleteNode(Node* p)
{
bool res = false;
if (p == NULL)
{
return false;
}
if (p->left == NULL && p->right == NULL) //删除没有子节点的节点
{
if (p->parent->left == p)
p->parent->left = NULL;
else
p->parent->right = NULL;
delete p;
p = NULL;
return true;
}
else if (p->left == NULL) //删除只有右子节点的节点
{
if (p == p->parent->left)
{
p->parent->left = p->right;
delete p;
p = NULL;
return true;
}
else
{
p->parent->right = p->right;
delete p;
p = NULL;
return true;
}
}
else if (p->right == NULL) //删除只有左子节点的节点
{
if (p == p->parent->left)
{
p->parent->left = p->left;
delete p;
p = NULL;
return true;
}
else
{
p->parent->right = p->left;
delete p;
p = NULL;
return true;
}
}
else //删除有两个子节点的节点
{
Node* minP = p->right;
while (minP->left)
{
minP = minP->left;
}
minP->data ^= p->data;
p->data ^= minP->data;
minP->data ^= p->data;
res = DeleteNode(minP); //转换成删除它
}
return res;
}
//删除数据
bool DeleteData(int data)
{
Node* p = search(data);
bool res = DeleteNode(p);
return res;
}
//提供给外部调用的中序遍历函数
void LNRshow()
{
LNRshow1(root);
std::cout << std::endl;
}
//提供给外部调用的层次遍历
void ShowByLevel()
{
if (root == NULL)
return;
std::queue<Node*> q;
Node* p = NULL;
q.push(root);
while (!q.empty())
{
p = q.front();
std::cout<< p->data<<" ";
q.pop();
if(p->left != NULL)
q.push(p->left);
if(p->right != NULL)
q.push(p->right);
}
std::cout << std::endl;
}
};
实现需要注意的点:
1、删除具有两个子节点的节点p。首先找到该节点右子树最小的节点minP,将该p与minP的数据交换,因为minP的值比左子树的节点大,又比右子树其它节点小,所以适合做根节点。接着问题转为删除minP节点,直接递归删除就行了。
2、任何需要释放节点的操作都要注意把父节点指向它的指针置NULL。同理,生成新的节点必须设置其父节点指针。
3、用非递归方法进行中序遍历比较难,代码如下:
void BSTree::UnResLNRShow()
{
stack<Node*> s;
Node* p = root;
while(p != NULL || !s.empty())
{
while(p != NULL)
{
s.push(p);
p = p->left;
}
if(!s.empty())
{
p = s.top();
s.pop();
std::cout<<p->data<<" ";
p = p->right;
}
}
std::cout<<std::endl;
}
基本思路就是先寻找最小的节点,依次入栈,从最小节点开始,先输出该节点的值,然后将右子树作为新的遍历目标遍历,用栈代替递归。其实没啥意义。
4、按层次遍历,先遍历第一层,接着遍历第二层,依次类推。需要用到队列,根节点先入列,然后每次出列,都把该出列的节点的左节点和右节点入列。
5、中序遍历输出的数据是排好序的,可以使用中序遍历的方法判断一个二叉树是不是二叉排序树。
6、实现拷贝构造函数和析构函数需要进行递归遍历。