查找二叉树:
一、定义
维基百科:
https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%85%83%E6%90%9C%E5%B0%8B%E6%A8%B9
二、代码:
1、结构体:
struct Node
{
int value;
Node *lchild,*rchild;
Node(int _value)
{
value = _value;
lchild = NULL;
rchild = NULL;
}
};
2、搜索元素:
(1)递归:
Node *search(Node *root,int x) //在二叉查找树中查找关键码(这里是value)的结点
{ //不成功返回NULL
if(root == NULL) return NULL;
else if(x == root->value) return root;
else if(x < root->value) return search(root->lchild,x);
else return search(root->rchild,x);
}
(2)非递归:
成功则返回该结点及其双亲结点,否则返回NULL,father为要插入结点的父结点
Node *search_iter(Node *root,int x,Node *&father) //非递归算法
{ //成功则返回该结点及其双亲结点,否则返回NULL,father为要插入结点的父结点
Node *p = root;
father = NULL;
while(p != NULL && p->value != x)
{
father = p;
if(x < p->value)
{
p = p->lchild;
}
else
{
p = p->rchild;
}
}
return p;
}
3、插入元素:
bool insert(Node *&root,int x)
{//向以根为*root的二叉查找树插入一个关键码为x的结点,如果root为空插入新结点为根结点
//如果已存在则不插入,否则插入,返回插入状态
Node *p,*father;
p = search_iter(root,x,father);
if(p != NULL) return false; //查找成功不插入
Node *newNode = new Node(x);
if(root == NULL) root = newNode;
else if(x < father->value) father->lchild = newNode;
else father->rchild = newNode;
return true;
}
4、创建查找二叉树:
通过插入来创建
void createBST(Node *&root,vector<int>values)
{
root = NULL;
for(int i = 0; i < values.size(); i++)
{
insert(root,values[i]);
}
}
5、水平树形打印:
void printBST(Node *root,int k)
{
if(root != NULL)
{
printBST(root->rchild,k+5); //先打右子树
for(int i = 0; i < k; i++)
{
cout<<" ";
}
cout<<root->value<<endl;
printBST(root->lchild,k+5);
}
}
6、中序遍历可得到升序序列:
void printOrder(Node *root)
{
if(root != NULL)
{
printOrder(root->lchild);
cout<<root->value<<",";
printOrder(root->rchild);
}
}
7、删除(最麻烦)😗*
事实上可以分为两种情况:
(1)
被删结点有一个子女或没有一个子女;
(2)
被删结点有两个子女;
设被删结点为*p
,被删结点的双亲为*father
,被删结点的子女为*son
(一个,如果一开始时是两个那就处理成一个)**
第(1)
种情况处理:
第(1)
种简单点,如果有一个子女,那么:
if(father->lchild == p)
{
father->lchild = son;
}
else
{
father->rchild = son;
}
如果*p
没有子女:
son = NULL;
if(father->lchild == p)
{
father->lchild = son;
}
else
{
father->rchild = son;
}
还需要注意的是,被删节点可能是根结点root
,那么father = NULL
,因此,令root=son
即可。
第(2)
种情况处理:
第(2)
种就复杂点了,因为如果*p
有两个结点那么就得保证在删去*p
之后,为保持其它元素之间的相对位置不变,确保查找二叉树的性质不变。有两种方法:
1)
在*p
右子树下找中序遍历时第一个结点,它的值是右子树下最小的;
2)
在*p
的左子树下找中序遍历的最后一个结点,它的值是左子树下最大的;
选其中一种方法,把这个结点的值填充到要删除的结点*p
,然后p
指向这个结点,father
指向这个结点的双亲,son
指向这个结点的子女 ( 如果是方法1)
,这个结点没有左子女;如果是方法2)
,这个结点没有左右子女,这个结点现在被p
指着现在),所以现在被删结点只有一个或没有一个子女了,那么就可以使用第(1)
种情况的方法来处理了。
代码实现:
bool deleteNode(Node *&root,int x)
{//在*root为根结点的二叉查找树中删除关键码为x的结点,删除成功函数返回true,否则返回false
Node *p; //指向待删结点
Node *son; //指向待删结点的子女(当然如果都空就是NULL)
Node *father; //查找后为待删结点的双亲结点
if((p = search_iter(root,x,father)) == NULL) return false; //查找失败不删除
if(p->lchild != NULL && p->rchild != NULL) //第(2)种情况左右子女不为空
{
son = p->rchild; //找p结点的右子树的中序后继(右子树下值最小的)
father = p;
while(son->lchild != NULL ) //找到退出循环
{
father = son;
son = son->lchild;
} //找到时son指向用来代替待删的结点(开始那个有左右结点的子女),father指向该结的双亲
p->value = son->value; //代替
p = son;//用 p来指向待删结点,p->lchild = NULL,son下一步指向待删结点的右子女(即使为空)
}
//son需要指向待删结点的子女(当然如果都空就是NULL)
if(p->lchild != NULL) son = p->lchild; //开始时是单子女(并且是左子女才会执行
else son = p->rchild; //如果一开始p是双子女,在上面也会处理为单子女(左子女为空)
if(p == root) //如果待删的结点是根结点,只出现在开始就是情况(1)
{ //因为如果是一开始是情况(2),在上面处理完后,删的结点肯定是root的子女(子孙)了
root = son;
}
else if(father->lchild == p)
{
father->lchild = son;
}
else
{
father->rchild = son;
}
delete p;
return true;
}
8、全部代码+测试:
#include<iostream>
#include<vector>
using namespace std;
struct Node
{
int value;
Node *lchild,*rchild;
Node(int _value)
{
value = _value;
lchild = NULL;
rchild = NULL;
}
};
Node *search(Node *root,int x) //在二叉查找树中查找关键码(这里是value)的结点
{ //不成功返回NULL
if(root == NULL) return NULL;
else if(x == root->value) return root;
else if(x < root->value) return search(root->lchild,x);
else return search(root->rchild,x);
}
Node *search_iter(Node *root,int x,Node *&father) //非递归算法
{ //成功则返回该结点及其双亲结点,否则返回NULL,father为要插入结点的父结点
Node *p = root;
father = NULL;
while(p != NULL && p->value != x)
{
father = p;
if(x < p->value)
{
p = p->lchild;
}
else
{
p = p->rchild;
}
}
return p;
}
bool insert(Node *&root,int x)
{//向以根为*root的二叉查找树插入一个关键码为x的结点,如果root为空插入新结点为根结点
//如果已存在则不插入,否则插入,返回插入状态
Node *p,*father;
p = search_iter(root,x,father);
if(p != NULL) return false; //查找成功不插入
Node *newNode = new Node(x);
if(root == NULL) root = newNode;
else if(x < father->value) father->lchild = newNode;
else father->rchild = newNode;
return true;
}
void createBST(Node *&root,vector<int>values)
{
root = NULL;
for(int i = 0; i < values.size(); i++)
{
insert(root,values[i]);
}
}
void printBST(Node *root,int k)
{
if(root != NULL)
{
printBST(root->rchild,k+5);
for(int i = 0; i < k; i++)
{
cout<<" ";
}
cout<<root->value<<endl;
printBST(root->lchild,k+5);
}
}
void printOrder(Node *root)
{
if(root != NULL)
{
printOrder(root->lchild);
cout<<root->value<<",";
printOrder(root->rchild);
}
}
bool deleteNode(Node *&root,int x)
{//在*root为根结点的二叉查找树中删除关键码为x的结点,删除成功函数返回true,否则返回false
Node *p; //指向待删结点
Node *son; //指向待删结点的子女(当然如果都空就是NULL)
Node *father; //查找后为待删结点的双亲结点
if((p = search_iter(root,x,father)) == NULL) return false; //查找失败不删除
if(p->lchild != NULL && p->rchild != NULL) //第(2)种情况左右子女不为空
{
son = p->rchild; //找p结点的右子树的中序后继(右子树下值最小的)
father = p;
while(son->lchild != NULL ) //找到退出循环
{
father = son;
son = son->lchild;
} //找到时son指向用来代替待删的结点(开始那个有左右结点的子女),father指向该结的双亲
p->value = son->value; //代替
p = son;//用 p来指向待删结点,p->lchild = NULL,son下一步指向待删结点的右子女(即使为空)
}
//son需要指向待删结点的子女(当然如果都空就是NULL)
if(p->lchild != NULL) son = p->lchild; //开始时是单子女(并且是左子女才会执行
else son = p->rchild; //如果一开始p是双子女,在上面也会处理为单子女(左子女为空)
if(p == root) //如果待删的结点是根结点,只出现在开始就是情况(1)
{ //因为如果是一开始是情况(2),在上面处理完后,删的结点肯定是root的子女(子孙)了
root = son;
}
else if(father->lchild == p)
{
father->lchild = son;
}
else
{
father->rchild = son;
}
delete p;
return true;
}
int main()
{
vector<int>values = {53,17,78,9,45,65,94,23,81,95};
Node *root = NULL;
createBST(root,values);
printBST(root,0);
cout<<endl<<endl;
printOrder(root);
cout<<endl<<endl;
deleteNode(root,78);
printOrder(root);
cout<<endl<<endl;
printBST(root,0);
cout<<endl<<endl;
return 0;
}