网上能够查到的数据结构相关资料,多是用C语言代码实现。
C语言是面向过程的,C++是面向对象的,尽管它们有诸多的相同之处,尽管把C语言实现BST的代码拷贝在C++中差不多就能pass了。但既然学习了C++,就尽可能使用面向对象的思想,将函数封装在类中,而不是在main函数外面写了一个又一个的函数。那样的话,即使是用了一些C++的语法,整个程序也可以说是标准的C语言式。
BST,Binary Search Tree,是一种很典型并且广泛使用的数据结构。它的特点是左边节点的数据值比该节点小,右边节点的数据值比该节点大。利用这一点,在AVL Tree(这是BST的升级版,以后再写)中可以实现O(log n)的查找效率。
在这边,BSTree类提供了插入节点、删除节点、搜索节点、判断空树,以及四中遍历方式等功能(修改?搜索出来就可以改了)
其中建树、添加节点、遍历等,需要注意的点都不多,唯独删除节点。
- 因为删除的节点只能是目标节点一个,而对其下的子树(若有),所有的节点必须保留。
- 而且删除目标节点之后,还需要保证该二叉树还是BST。
至于删除目标节点,以及其下所有子孙节点的函数,实现起来比较简单。有需要的慕友可自行增加这一函数。
代码设计:
- 节点类。一些函数在节点类中比较容易实现,把这个任务交给它。还有一些辅助函数,删除节点函数所需要的,也在节点类中设计。
- 树类。提供建一颗空树、判空、根据数据增加节点、查找、删除的功能,以及前序、中序、后序、层序,四种遍历。
- 测试
BSTree类的头文件BSTree.h以及实现文件BSTree.cpp如下:
#pragma once
#include "Node.h"
class BSTree {
public:
BSTree();//建一颗空树。
bool isEmpty(); //判断树是否为空。
void insertNode(ElemType &elem); //传入元素值,插入一个节点
bool deleteNode(ElemType &elem); //删除给定元素值的节点。
Node* searchNode(ElemType &elem); //查找给定元素值的节点,返回其地址。
void preOrderTaversal(); //前序、中序、后序以及层序遍历。
void inOrderTaversal();
void postOrderTaversal();
void levelOrderTaversal();
private:
Node *rootPtr;
};
#include "BSTree.h" #includeBSTree::BSTree() { this->rootPtr = NULL; } bool BSTree::isEmpty() { return NULL == rootPtr; } void BSTree::insertNode(ElemType &elem) { if (isEmpty()) rootPtr = new Node(elem); //若树非空,插入一个新节点的操作在Node类中更容易实现,所以把这个任务交给Node类。 else rootPtr->addNode(elem); } bool BSTree::deleteNode(ElemType &elem) { Node *objNode = searchNode(elem); if (NULL == objNode) return false; //树根节点的情况比较特殊,单独考虑。 if (objNode == rootPtr && !(rootPtr->leftChild != NULL && rootPtr->rightChild != NULL)) { if (rootPtr->leftChild == NULL && rootPtr->rightChild == NULL) rootPtr = NULL; else if (rootPtr->leftChild == NULL) { //rootPtr->rightChild != NULL rootPtr = rootPtr->rightChild; rootPtr->parent = NULL; } else if (rootPtr->rightChild == NULL) { rootPtr = rootPtr->leftChild; rootPtr->parent = NULL; } delete objNode; //树根有两个子节点的情况可做一般性处理。 } else objNode->eraseNode(); return true; } Node* BSTree::searchNode(ElemType &elem) { if (isEmpty()) return NULL; //若树非空,搜索指定元素值的节点在Node类中也更容易实现,同样将这个任务交给Node类。 return rootPtr->findNode(elem); } void BSTree::preOrderTaversal() { if (!isEmpty()) rootPtr->traversalByPreOrder(); } void BSTree::inOrderTaversal() { if (!isEmpty()) rootPtr->traversalByInOrder(); } void BSTree::postOrderTaversal() { if (!isEmpty()) rootPtr->traversalByPostOrder(); } void BSTree::levelOrderTaversal() { if (!isEmpty()) rootPtr->traversalByLevelOrder(); }
Node类的头文件和实现文件如下:
#pragma once
typedef int ElemType;
class Node {
public:
Node(ElemType data = 0);
void addNode(ElemType &elem);
Node* findNode(ElemType &elem); //返回给定元素值的节点的地址,若找不到,返回NULL。
Node* findMin(); //返回以此节点为根节点的树的元素值最小的节点。
Node* findMax();
void eraseNode();
void traversalByPreOrder(); //前序、中序、后序以及层序遍历以此节点为根节点的树。
void traversalByInOrder();
void traversalByPostOrder();
void traversalByLevelOrder();
public:
ElemType data;
Node *parent;
Node *leftChild;
Node *rightChild;
};
#include "Node.h" #include#include using namespace std; Node::Node(ElemType data ) { this->data = data; parent = leftChild = rightChild = NULL; } void Node::addNode(ElemType &elem) { if (elem < data) { if (leftChild == NULL) { leftChild = new Node(elem); leftChild->parent = this; } else leftChild->addNode(elem); } else if (elem > data) { if (rightChild == NULL) { rightChild = new Node(elem); rightChild->parent = this; } else rightChild->addNode(elem); } //do nothing when 'elem == data'. } Node* Node::findNode(ElemType &elem) { if (elem == data) return this; else if (elem < data && leftChild != NULL) return leftChild->findNode(elem); else if (elem > data && rightChild != NULL) return rightChild->findNode(elem); return NULL; } Node* Node::findMin() { Node *tmp = this; while (tmp->leftChild != NULL) tmp = tmp->leftChild; return tmp; } Node* Node::findMax() { Node *tmp = this; while (tmp->rightChild != NULL) tmp = tmp->rightChild; return tmp; } void Node::eraseNode() { if (leftChild != NULL && rightChild != NULL) { Node *tmp = leftChild->findMax(); data = tmp->data; tmp->eraseNode(); } else { if (leftChild == NULL) { //左孩子节点是NULL,有右孩子节点,或者没有子节点。 if (this == parent->leftChild) parent->leftChild = rightChild; else parent->rightChild = rightChild; if (rightChild != NULL) rightChild->parent = parent; } else { //有左孩子,右孩子为NULL if (this == parent->leftChild) parent->leftChild = leftChild; else parent->rightChild = leftChild; leftChild->parent = parent; } } } void Node::traversalByPreOrder() { cout << data << " "; if (leftChild != NULL) leftChild->traversalByPreOrder(); if (rightChild != NULL) rightChild->traversalByPreOrder(); } void Node::traversalByInOrder() { if (leftChild != NULL) leftChild->traversalByInOrder(); cout << data << " "; if (rightChild != NULL) rightChild->traversalByInOrder(); } void Node::traversalByPostOrder() { if (leftChild != NULL) leftChild->traversalByPostOrder(); if (rightChild != NULL) rightChild->traversalByPostOrder(); cout << data << " "; } void Node::traversalByLevelOrder() { queue que; que.push(this); while (!que.empty()) { Node *tmp = que.front(); que.pop(); cout << tmp->data << " "; if (tmp->leftChild != NULL) que.push(tmp->leftChild); if (tmp->rightChild != NULL) que.push(tmp->rightChild); } }
测试文件如下:
#include "BSTree.h" #includeusing namespace std; int main(void) { BSTree *bst = new BSTree; cout << boolalpha << "空树?" << bst->isEmpty() << endl; int intArr[] = { 6, 5, 8, 4, 7, 9, 2, 1, 3 }; for (int i = 0; i < sizeof(intArr) / sizeof(int); i++) { bst->insertNode(intArr[i]); } cout << boolalpha << "空树?" << bst->isEmpty() << endl; //测试 deleteNode() 函数 int elem = 10; bst->deleteNode(elem); elem = 3; bst->deleteNode(elem); elem = 4; bst->deleteNode(elem); elem = 8; bst->deleteNode(elem); elem = 6; bst->deleteNode(elem); bst->preOrderTaversal(); cout << "先序遍历" << endl; bst->inOrderTaversal(); cout << "中序遍历" << endl; bst->postOrderTaversal(); cout << "后序遍历" << endl; bst->levelOrderTaversal(); cout << "层序遍历" << endl; delete bst; system("pause"); return 0; }
慕友也可将之修改成一个类模板,但要切记,许多编译器不支持类模板的头文件和实现文件分离,要把它们都放在头文件中。



