参考资料:《数据结构与算法分析》(第三版英文版)
书本题目:5.15
其实是做作业的时候顺便复习一下二叉查找树,顺便把自己写代码时的过程记录下来。虽然代码可能比较丑,但应该对于新手而言是比较友好的,可以了解如何一步一步实现BST的函数的。同时更深入理解一下返回值为Node*类型的Help函数。
说明:BST为Binary Search Tree,二叉查找树.Node为节点,即二叉查找树的节点。代码加粗部分为每次新增的代码
首先建立Node节点,每个节点只有1个整数类型的public数据成员
#pragma once
#include <iostream>
using namespace std;
class Node
{
public:
int x;
Node* left;
Node* right;
Node(int val, Node* l = NULL, Node* r = NULL) {
x = val;
left = l;
right = r;
}
~Node() {};
};
1。初始化
我们首先要实现构造函数和析构函数。
构造函数其实就是将root指向新new出来的Node对象。
析构函数中借用了一个clearHelp()函数,其参数为任意BST)的将被删除一个根节点rt(因为这样才能实现递归)。使用递归从左子树遍历到右子树,当发现某一节点的左右子树均为空的时候,删除该节点。随后逆流而上,向上删除。
#pragma once
#include "Node.h"
class BST
{
public:
Node* root;
BST(int val) {
root = new Node(val, NULL, NULL);
}
~BST() {
clearHelp(root);
}
void clearHelp(Node* rt) {
if (rt == NULL) return;
clearHelp(rt->left);
clearHelp(rt->right);
delete rt;
}
};
实现插入函数insert();
首先,在Node类里面分别实现设置左节点和右节点的setleft()和setRight()函数
///Node 类
#pragma once
#include <iostream>
using namespace std;
class Node
{
public:
int x;
Node* left;
Node* right;
Node(int val, Node* l = NULL, Node* r = NULL) {
x = val;
left = l;
right = r;
}
~Node() {};
void setLeft(Node* l) {
left = l;
}
void setRight(Node* r) {
right = r;
}
};
随后在BST类里面在InsertHelp(Node* ,int)函数的帮助下完成insert(int)函数.。
原理(递归):从根节点出发,首先检测该节点是否为空,为空则返回一个新创建的节点。若不为空,则将要被插入的值val与当前按节点的值x比较,当val小于x时,向左继续往下走,否则向右继续往下走,进行新一轮检测。在新一轮检测中,把当前根节点的rt的左孩子rt->left作为新的根节点
再次调用
insertHelp里面有3个重要的地方值得关注。
第一个是nsertHelp的返回类型是Node* 类型。
第二个是当前根节点不为空的setLeft()。这个是为了保证插入val之后维护好BST的用的。这要自己慢慢体会。
第三个是节点为空NULL时的返回
#pragma once
#include "Node.h"
class BST
{
public:
Node* root;
BST(int val) {
root = new Node(val, NULL, NULL);
}
~BST() {
clearHelp(root);
}
void clearHelp(Node* rt) {
if (rt == NULL) return;
clearHelp(rt->left);
clearHelp(rt->right);
delete rt;
}
void insert(int val)
{
insertHelp(root, val);
}
Node* insertHelp(Node* rt, int val) {
if (root == NULL)
return new Node(val, NULL, NULL);
if (val < rt->x)
{
rt->setLeft(insertHelp(rt->left, val));
}
else
{
rt->setRight(insertHelp(rt->right, val));
}
}
};
3。删
删除整个树的最小值,先用一根指针指向含有最小值的节点,再将这个节点的父节点的左指针指向这个节点的右孩子,而不在意这个节点的右孩子是否为空。
同样注意递归,setLeft(),以及Help返回类型为Node*这三个要点。
#pragma once
#include "Node.h"
class BST
{
public:
Node* root;
BST(int val) {
root = new Node(val, NULL, NULL);
}
~BST() {
clearHelp(root);
}
void clearHelp(Node* rt) {
if (rt == NULL) return;
clearHelp(rt->left);
clearHelp(rt->right);
delete rt;
}
void insert(int val)
{
insertHelp(root, val);
}
Node* insertHelp(Node* rt, int val) {
if (root == NULL)
return new Node(val, NULL, NULL);
if (val < rt->x)
{
rt->setLeft(insertHelp(rt->left, val));
}
else
{
rt->setRight(insertHelp(rt->right, val));
}
}
Node* findMin(Node* rt) {
if (rt->left==NULL) return rt;
else
return findMin(rt->left);
}
Node* deleteMin(Node* rt) {
if (rt->left == NULL)
return rt->right;
else {
rt->setLeft(deleteMin(rt->left));
return rt;
}
}
void deleteMin(){
Node *temp = findMin(root);
root = deleteMin(root);
delete temp;
}
};
删除带有特定值val的节点
同样注意递归,setLeft(),以及返回类型为Node*这三个要点。
#pragma once
#include "Node.h"
class BST
{
public:
Node* root;
BST(int val) {
root = new Node(val, NULL, NULL);
}
~BST() {
clearHelp(root);
}
void clearHelp(Node* rt) {
if (rt == NULL) return;
clearHelp(rt->left);
clearHelp(rt->right);
delete rt;
}
void insert(int val)
{
insertHelp(root, val);
}
Node* insertHelp(Node* rt, int val) {
if (root == NULL)
return new Node(val, NULL, NULL);
if (val < rt->x)
{
rt->setLeft(insertHelp(rt->left, val));
}
else
{
rt->setRight(insertHelp(rt->right, val));
}
}
Node* findMin(Node* rt) {
if (rt->left==NULL) return rt;
else
return findMin(rt->left);
}
Node* deleteMin(Node* rt) {
if (rt->left == NULL)
return rt->right;
else {
rt->setLeft(deleteMin(rt->left));
return rt;
}
}
void deleteMin(){
Node *temp = findMin(root);
root = deleteMin(root);
delete temp;
}
Node* removeHelp(Node* rt, int val)
{
if (rt == NULL) return NULL;
if (val < rt->x) rt->setLeft(removeHelp(rt->left, val));
if (val > rt->x) rt->setRight(removeHelp(rt->right, val));
else {
Node* temp = rt;
if (rt->left == NULL) {
rt = rt->right;
delete temp;
}
else if (rt->right == NULL) {
rt = rt->left;
delete temp;
}
else //左右节点均为空或者左右节点均不为空
{
Node* temp = findMin(rt->right); //找到右子树最小值
rt->x = temp->x; //将右子树的最小值赋给rt值
rt->setRight(deleteMin(rt->right)); // 将rt的右子树设为删除最小值后的新右子数
delete temp;
}
}
return rt;
}
void remove(int val)
{
root = removeHelp(root, val);
}
};
4。查
查找是否含有特定值val的节点,有则返回true,否则返回false.
#pragma once
#include "Node.h"
class BST
{
public:
Node* root;
BST(int val) {
root = new Node(val, NULL, NULL);
}
~BST() {
clearHelp(root);
}
void clearHelp(Node* rt) {
if (rt == NULL) return;
clearHelp(rt->left);
clearHelp(rt->right);
delete rt;
}
void insert(int val)
{
insertHelp(root, val);
}
Node* insertHelp(Node* rt, int val) {
if (root == NULL)
return new Node(val, NULL, NULL);
if (val < rt->x)
{
rt->setLeft(insertHelp(rt->left, val));
}
else
{
rt->setRight(insertHelp(rt->right, val));
}
}
Node* findMin(Node* rt) {
if (rt->left==NULL) return rt;
else
return findMin(rt->left);
}
Node* deleteMin(Node* rt) {
if (rt->left == NULL)
return rt->right;
else {
rt->setLeft(deleteMin(rt->left));
return rt;
}
}
void deleteMin(){
Node *temp = findMin(root);
root = deleteMin(root);
delete temp;
}
Node* removeHelp(Node* rt, int val)
{
if (rt == NULL) return NULL;
if (val < rt->x) rt->setLeft(removeHelp(rt->left, val));
if (val > rt->x) rt->setRight(removeHelp(rt->right, val));
else {
Node* temp = rt;
if (rt->left == NULL) {
rt = rt->right;
delete temp;
}
else if (rt->right == NULL) {
rt = rt->left;
delete temp;
}
else
{
Node* temp = findMin(rt->right);
rt->x = temp->x;
rt->setRight(deleteMin(rt->right));
delete temp;
}
}
return rt;
}
void remove(int val)
{
root = removeHelp(root, val);
}
Node* findHelp(Node* rt, int val)
{
if (rt == NULL) return NULL;
else if (val < rt->x) return findHelp(rt->left, val);
else if (val > rt->x) return findHelp(rt->right, val);
else return rt;
}
bool is_Exist(int val) {
if (findHelp(root, val) == NULL) return false;
else return true;
}
};
5。改,其实到这里大家应该都知道怎么写了,在findHelp函数的帮助下找到要被替换的值,对其进行修改。若找不到,则插入新值。
6。前序遍历,中序遍历,后序遍历 (其实是以 根节点的位置来命名这三种遍历的),下面看代码
//前序遍历 根->左->右
void preorderTraverseHelp(Node* rt) {
if (rt == NULL) return ;
//else activity(rt); 当前节点不为空的话,对当前节点进行操作,这个操作是任意的,
cout << rt->x<<endl; // 如对当前节点的值进行输出。
preorderTraverseHelp(rt->left);
preorderTraverseHelp(rt->right);
}
//实现函数封装
void preorderTraverse() {
preorderTraverseHelp(root);
}
//中序遍历 左->根->右
void inorderTraverseHelp(Node* rt) {
if (rt == NULL) return;
inorderTraverseHelp(rt->left);
cout << rt->x << endl;
inorderTraverseHelp(rt->right);
}
void inorderTraver() {
inorderTraverseHelp(root);
}
//后序遍历 左->右->根
void postorderTraverseHelp(Node* rt)
{
if (rt == NULL) return;
postorderTraverseHelp(rt->left);
postorderTraverseHelp(rt->right);
cout << rt->x << endl;
}
void postorderTraverse()
{
postorderTraverseHelp(root);
}
7。做题。在main.cpp里面
#include "BST.h"
int main() {
BST* tree = new BST(15);
tree->insert(20);
tree->insert(25);
tree->insert(18);
tree->insert(16);
tree->insert(5);
tree->insert(7);
cout << endl << "preorder:" <<endl;
tree->preorderTraverse();
cout << endl << endl << "inorder;"<<endl ;
tree->inorderTraver();
cout << endl << endl << "postorder:"<<endl ;
tree->postorderTraverse();
system("pause");
}
运行结果