2019-12-02
Data Structure 之 二叉排序树
二叉排序树是给定一个节点后,接下来插入的数如果比它大就会放到它的右孩子那边,比它小就会放到它的左孩子那边。
所以对于相同的一个节点下的左右孩子,左孩子 < 根节点 < 右孩子。
节点
由简单的左孩子和右孩子指针加上数据就可以构成一个节点。
//BST节点
struct BSTNode{
int data;
BSTNode *RightChild;
BSTNode *LeftChild;
BSTNode(){
RightChild = NULL;
LeftChild = NULL;
}
};
二叉排序树的类
多的功能不会只需要有一个root的根节点。
插入
插入操作是一个基本,写好后在建树的时候直接调用插入函数就可以将所有元素插入到树里面。
在这里有一个递归的思想但是没有用到函数递归调用的方法。
对于要插入的数,从根节点开始比较,如果比当前节点大就往当前节点右孩子方向走,否则相反。
void Insert(int num){
BSTNode *temp = root;//记录头结点位置
while(1){//模拟递归的遍历
if(num>temp->data){
if(temp->RightChild!=NULL){
temp=temp->RightChild;
}
else{//如果当前节点右孩子为NULL,则新建一个空间储存插入的数据
temp->RightChild = new BSTNode();
temp->RightChild->data = num;
break;
}
}
else{
if(temp->LeftChild!=NULL){
temp=temp->LeftChild;
}
else{
temp->LeftChild = new BSTNode();
temp->LeftChild->data = num;
break;
}
}
}
}
建树
写好了插入函数后建树就简单了,用数组num和数组大小当参数传递给creatBST函数。
将root节点的data赋值为num【0】,也就是数组第一个元素。
其他的元素用插入函数完成插入即可。
void creatBST(int num[],int n){
root = new BSTNode();
root->data = num[0];
for(int i=1;i<n;i++)
this->Insert(num[i]);
}
查找
查找的思路其实很简单,就是遍历一遍,这里下标采用中序遍历的计数。
int Find(int num){
BSTNode *temp = root;
int sum=0;
while(1){
if(temp->RightChild==NULL&&temp->LeftChild==NULL&&num!=temp->data){
return -1;
}
sum++;
if(num==temp->data){
return sum;
}
else if(num>temp->data&&temp->RightChild!=NULL){
temp=temp->RightChild;
}
else if(num<temp->data&&temp->LeftChild!=NULL)
temp = temp->LeftChild;
}
}
删除
今天写删除花了特别多时间,最后还是面向CSDN编程,研究一波后发现了别人很多bug,自己又改动了下。
这里我参考那篇文章的dl就是删除第一个节点的时候会有bug,删除这个函数真的很虚,感觉写的贼菜...
总觉得有很多思路,很多方法,但是写出来就很难看...
void Delete(int num){
BSTNode *temp, *previous;
temp = root;
previous = root;
//获得现在要删除的节点和它前一个节点
while (temp != NULL){
if (temp->data == num)
break;
else if (temp->data > num){
previous = temp;
temp = temp->LeftChild;
}
else{
previous = temp;
temp = temp->RightChild;
}
}
if (temp != NULL){
if (temp->LeftChild == NULL){ //要删的结点的左孩子为空的情况
if (temp->RightChild==NULL){//右孩子也为空
if(previous==root) root = NULL; //如果是头结点只需把roor设为NULL
else{
previous->LeftChild==temp? previous->LeftChild = NULL: previous->RightChild = NULL;
delete temp;
}
}
else{//有右孩子,判断该节点是不是根节点或者是上一个节点的什么孩子
if(temp==root) root=root->RightChild;
else previous->LeftChild==temp ? previous->LeftChild=temp->RightChild :previous->RightChild=temp->RightChild;
delete temp;
}
}
else if (temp->RightChild == NULL){ //要删的结点的右孩子为空的情况,但是有左孩子
if(temp=root) root = root->LeftChild;
else previous->LeftChild == temp ? previous->LeftChild=temp->LeftChild :previous->RightChild=temp->LeftChild;
delete temp;
}
else{ //要删的结点的左、右孩子都不为空的情况
//把右子树最小节点的值赋给当前节点,删去右子树最小节点 ,右子树最小节点的右节点顶替原最小节点位置
BSTNode *right_min = temp->RightChild;
previous = right_min;
while (right_min->LeftChild != NULL){ //找到右子树最小结点
previous = right_min;
right_min = right_min->LeftChild;
}
temp->data = right_min->data; //最小结点的值赋给要删除的结点
if(right_min == previous){
temp->RightChild=right_min->RightChild;
}
else{
previous->LeftChild = right_min->RightChild;
}
delete right_min; //删除右子树的最小结点
}
}
}
插入、查找、删除样例如下:
/* 1 6 22 33 55 66 11 44 3 77 50 10 */ /*1 6 22 33 55 66 11 44 7 11 22 33 44 55 66 77 */ /* 1 6 22 33 55 66 11 44 3 66 22 77 */
最后全部代码如下:
#include<iostream>
using namespace std;
//BST节点
struct BSTNode{
int data;
BSTNode *RightChild;
BSTNode *LeftChild;
BSTNode(){
RightChild = NULL;
LeftChild = NULL;
}
};
class BST{
private:
BSTNode *root;//根节点
void InOrder(BSTNode *t){//中序遍历
if(t!=NULL){
InOrder(t->LeftChild);
cout<<t->data<<" ";
InOrder(t->RightChild);
}
}
public:
BST(){
}
~BST(){
}
void creatBST(int num[],int n){
root = new BSTNode();
root->data = num[0];
for(int i=1;i<n;i++)
this->Insert(num[i]);
}
void InOrder(){
if(root){
InOrder(root);
cout<<endl;
}
else
cout<<"No elements"<<endl;
}
void Insert(int num){
BSTNode *temp = root;
while(1){
if(num>temp->data){
if(temp->RightChild!=NULL){
temp=temp->RightChild;
}
else{
temp->RightChild = new BSTNode();
temp->RightChild->data = num;
break;
}
}
else{
if(temp->LeftChild!=NULL){
temp=temp->LeftChild;
}
else{
temp->LeftChild = new BSTNode();
temp->LeftChild->data = num;
break;
}
}
}
}
int Find(int num){
BSTNode *temp = root;
int sum=0;
while(1){
if(temp->RightChild==NULL&&temp->LeftChild==NULL&&num!=temp->data){
return -1;
}
sum++;
if(num==temp->data){
return sum;
}
else if(num>temp->data&&temp->RightChild!=NULL){
temp=temp->RightChild;
}
else if(num<temp->data&&temp->LeftChild!=NULL)
temp = temp->LeftChild;
}
}
void Delete(int num){
BSTNode *temp, *previous;
temp = root;
previous = root;
//获得现在要删除的节点和它前一个节点
while (temp != NULL){
if (temp->data == num)
break;
else if (temp->data > num){
previous = temp;
temp = temp->LeftChild;
}
else{
previous = temp;
temp = temp->RightChild;
}
}
if (temp != NULL){
if (temp->LeftChild == NULL){ //要删的结点的左孩子为空的情况
if (temp->RightChild==NULL){//右孩子也为空
if(previous==root) root = NULL; //如果是头结点只需把roor设为NULL
else{
previous->LeftChild==temp? previous->LeftChild = NULL: previous->RightChild = NULL;
delete temp;
}
}
else{//有右孩子,判断该节点是不是根节点或者是上一个节点的什么孩子
if(temp==root) root=root->RightChild;
else previous->LeftChild==temp ? previous->LeftChild=temp->RightChild :previous->RightChild=temp->RightChild;
delete temp;
}
}
else if (temp->RightChild == NULL){ //要删的结点的右孩子为空的情况,但是有左孩子
if(temp=root) root = root->LeftChild;
else previous->LeftChild == temp ? previous->LeftChild=temp->LeftChild :previous->RightChild=temp->LeftChild;
delete temp;
}
else{ //要删的结点的左、右孩子都不为空的情况
//把右子树最小节点的值赋给当前节点,删去右子树最小节点 ,右子树最小节点的右节点顶替原最小节点位置
BSTNode *right_min = temp->RightChild;
previous = right_min;
while (right_min->LeftChild != NULL){ //找到右子树最小结点
previous = right_min;
right_min = right_min->LeftChild;
}
temp->data = right_min->data; //最小结点的值赋给要删除的结点
if(right_min == previous){
temp->RightChild=right_min->RightChild;
}
else{
previous->LeftChild = right_min->RightChild;
}
delete right_min; //删除右子树的最小结点
}
}
}
};
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
int num[100];
for(int i=0;i<n;i++)
cin>>num[i];
BST *bst = new BST();
bst->creatBST(num,n);
bst->InOrder();
int m;
cin>>m;
int b[100];
for(int i=0;i<m;i++){
cin>>b[i];
//bst->Insert(b[i]);
//cout<<bst->Find(b[i])<<endl;
bst->Delete(b[i]);
bst->InOrder();
}
}
}