基础简单的数据结构-C语言-二叉查找树数组模拟

二叉查找树,它亦或是一颗空树,在或者是有以下性质的二叉树


1:某个结点左子树存在则该左孩子数值必小于该结点数值
2:某个结点右子树存在则该右孩子数值必不小于该结点数值

写代码之前,需要了解两个概念

一:后继结点
某个结点比如Q的后继结点也就是将Q所在的树进行中序排序后排序在Q后面的一个结点。
如果一个结点Q有右孩子,那么中序排序的时候,这个结点的右子树的最左节节点会出现Q后面,如果没有右孩子,那么去寻找Q结点的父辈结点X,这个X需要符合其属于其父结点M的左孩子。那么这个结点M在中序排序时会出现在Q后面。自己想想中序遍历是怎样的就好了

二:删除结点Q出现的情况

1:该节点没有孩子,那么直接删除这个节点Q就好了,并清除相应关系,比如其父节点指向其的指针需要清除

2:如果只有一个孩子,那么把Q的父节点和Q的那个孩子进行连接,并配置好两者之间的关系,比如是左孩子还是

右孩子等并相应删除Q

3:如果有两个孩子,那么把这个节点的后继结点M找到,这时候删除M就好了而不是Q,且需要将Q的值改为M的值

——————————————————————————————————————————————

代码是用数组模拟链表结构(请耐心看删除那一部分,当然一定会有更好的写法):

#include<stdio.h>
#include<stdlib.h>
#define MAX_node 100
//分别代表父结点,数据,左子树,右子树
int p[MAX_node],data[MAX_node],lc[MAX_node],rc[MAX_node];
int root=-1;//初始化
int stack[MAX_node];//建立一个栈便于查找空位
int top=0;

/*初始化*/
void init(){
int i;
for(i=0;i<MAX_node;++i)
{
    p[i]=data[i]=lc[i]=rc[i]=-1;//默认空值为-1
    stack[i]=i;
}
}


/*正式插入数据*/
void tree_insert(int z)
{   //x为从根节点遍历插入的数据应该位于哪里
    int x=root,y=-1;//y的设定是为了保存最后存入数据位置的父结点
    while(x!=-1){//去寻找最后要插入的位置
        y=x;//保存最后插入位置对应的父结点
        if(data[z]<data[x])
            x=lc[x];
        else
            x=rc[x];
    }
    p[z]=y;//数据z的父节点就是y了
    if(y==-1)//第一个插入数据的情况(第一次插入时第一个数据就是根了!!!)
       root=z;
    else if(data[z]<data[y])
        lc[y]=z;
    else
        rc[y]=z;
    return ;

    }

/*插入预处理操作*/
void pre_insert(int nowdata)
{
int z;
z=stack[top++];//体现栈的作用
data[z]=nowdata;//将该值获取
tree_insert(z);//进行插入
}

/*搜索树上值为k的结点,返回数组下标*/
int tree_search(int x,int k)
{
while(x!=-1 && data[x]!=k){//迭代寻找
    if(k<data[x])
        x=lc[x];
    else
        x=rc[x];
}
return x;
}
/*返回最小值*/
int tree_minnum(int x)
{
while(lc[x]!=-1)
    x=lc[x];
return x;
}
/*求某节点的后继结点 为删除做准备*/
/*这里很关键*/
int tree_successor(int x)
{
    if(rc[x]!=-1)//如果该节点存在右子数,则直接寻找右子数的最左结点(这里删除操作需要用到)
        return tree_minnum(rc[x]);
    int y=p[x];
    //这里需要额外注意 如果没有右子树,则一直向上寻找,找到一个父辈的结点Q,
    //Q的父结点为W,Q正好为W的左子树,那么这个结点W就是后继结点了
    while(y!=-1 && x==rc[y])
    {
        x=y;
        y=p[y];
    }
    return y;
}

 /*正式删除*/
 void tree_delete(int z)
 {
int x,y;
//查看是否有孩子
if(lc[z]==-1 || rc[z]==-1)//有一个或者没有
    y=z;//用y拷贝一份
else
    y=tree_successor(z);//有两个
//如果没有孩子 x还是-1
//如果有一个孩子的话确定这个孩子是左是右
/*如果有两个孩子那么,此时y是后继结点,y这时是没有左子树的
否则不符合求后继结点,只会存在右子树,如果有,那么需要重新指向,没有就删除了
这个y是要被删除的
*/
if(lc[y]!=-1)
    x=lc[y];
else
    x=rc[y];

//(正式重新指向)如果真的z有一个孩子或者后继结点y有右子树孩子,那么此时这个孩子的父结点应该是y的
//父结点了。
if(x!=-1)
    p[x]=p[y];
//接下来需要删除这个y
if(p[y]==-1)//删除的是根结点(父节点不存在就是根了)
    root=x;
//这里重新指引与p[x]=p[y]不同,就好比我认你当孩子,你还要认我当父节点一样
//如果是两个孩子进入这里判断那么此时y自己有孩子就重新指引,没有就直接删除y
//如果是一个孩子进入这里判断那么这个孩子重新被指引了
//如果是没有孩子进入这里判断那么这个结点直接被删除了
else if(y==lc[p[y]])
    lc[p[y]]=x;
else
    rc[p[y]]=x;
//两个孩子情况,这时候要把数据赋值给要本要删除的结点即可
if(y!=z)
    data[z]=data[y];
/*最后删除可能还没有删除的y*/
data[y]=-1;
lc[y]=-1;
rc[y]=-1;
p[y]=-1;
stack[--top]=y;
return;
}

/*删除预处理操作*/
 void pre_delete(int data)
 {
 int k;
 k=tree_search(root,data);
 if(k!=-1)
    tree_delete(k);//找到这个数据下标位置并进行删除
 return;
 }
void display(int x)
{
    if(x==-1)
        return;
    if(lc[x]!=-1)
        display(lc[x]);

    printf("%4d%4d%4d%4d\t\n",data[x],lc[x],rc[x],p[x]);
    if(rc[x]!=-1)
        display(rc[x]);
    return;
}
int main(){

int i,t;
init();
/*
1 检查插入
2 检查删除
3 检查展示
4 寻找
*/
while(scanf("%d",&i)!=EOF)
{    switch(i){
case 1:
    printf("insert num:\n");
    scanf("%d",&t);
    pre_insert(t);
    printf("数据 左孩子 右孩子 父节点\n");
    display(root);
    break;
case 2:
    printf("delete num:\n");
    scanf("%d",&t);
    pre_delete(t);
    printf("数据 左孩子 右孩子 父节点\n");
    display(root);
    break;
case 3:
    printf("数据 左孩子 右孩子 父节点\n");
    display(root);
    break;
case 4:
    int a,b;
    printf("what num do you want to find?\n");
    scanf("%d",&a);
    b=tree_search(root,a);
    printf("该值下标为%d",b);
    break;
default:
    return 0;
}}
return 0;
}





    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/gaoapp/article/details/71090573
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞