一、二叉排序树的定义
二叉排序树(Binary Search Tree)是一种特殊的二叉树,它的左子树中的所有节点都比根节点小,右子树中的所有节点都比根节点大。
总之,对于树中的每个节点,其左子树中的所有节点的值都小于它,其右子树中的所有节点的值都大于它。
typedef struct Node { int data; struct Node *right,*left; }*BiTree;
二、二叉排序树的构造
1. 递归插入节点
二叉排序树可以通过递归插入节点来构造,在插入节点的时候需要比较节点的值与当前节点的值的大小关系,以此来确定节点的插入位置。
void insertBST(BiTree *root,int data) { if(!(*root)) { *root=(Node*)malloc(sizeof(Node)); (*root)->data=data; (*root)->left=(*root)->right=NULL; return; } if(data==(*root)->data) { return; } else if(data<(*root)->data) { insertBST(&(*root)->left,data); } else if(data>(*root)->data) { insertBST(&(*root)->right,data); } }
当插入一个节点时,如果二叉排序树为空,则将该节点作为根节点插入;
当插入的节点的值等于二叉排序树的根节点的值时,不进行任何操作;
当插入的节点的值小于二叉排序树的根节点的值时,递归地插入在左子树中;
当插入的节点的值大于二叉排序树的根节点的值时,递归地插入在右子树中。
2. 非递归插入节点
除了递归插入节点,我们还可以通过非递归方式插入节点来构造二叉排序树,方法是利用while循环周期性地遍历二叉排序树的节点,直到找到插入位置。
void insertBST(BiTree *root,int data) { BiTree p=*root,q; if(!p) { *root=(Node*)malloc(sizeof(Node)); (*root)->data=data; (*root)->left=(*root)->right=NULL; return; } while(p) { if(data==p->data) { return; } q=p; if(datadata) { p=p->left; } else { p=p->right; } } p=(BiTree)malloc(sizeof(Node)); p->data=data; p->left=p->right=NULL; if(data data) { q->left=p; } else { q->right=p; } }
先定义一个p指针指向根节点,如果p为空,则将新节点作为根节点插入;若p不为空,则进行while循环。对于每个节点p,我们比较插入的值与p的值大小,以此来确定插入位置。如果查找到相同节点的值,则不进行插入;如果移向了空节点,则插入新节点作为其子节点。
三、二叉排序树的遍历
遍历二叉排序树可以得到一个有序的数列,可以按照从小到大或从大到小的顺序输出节点值。
1. 中序遍历
中序遍历是一种常用的遍历方式,中序遍历二叉排序树可以得到有序的节点序列。
void inOrder(BiTree root) { if(root!=NULL) { inOrder(root->left); printf("%d ",root->data); inOrder(root->right); } }
从根节点开始,先遍历左子树,然后输出根节点,最后遍历右子树。
2. 前序遍历
前序遍历从根节点开始遍历,先输出根节点,然后访问左子树,最后遍历右子树。
void preOrder(BiTree root) { if(root!=NULL) { printf("%d ",root->data); preOrder(root->left); preOrder(root->right); } }
3. 后序遍历
后序遍历从左叶子结点开始后序遍历,然后是右子树,最后是根节点。
void postOrder(BiTree root) { if(root!=NULL) { postOrder(root->left); postOrder(root->right); printf("%d ",root->data); } }
四、二叉排序树的删除
二叉排序树的删除分为三种情况:
1. 被删除节点无子节点,直接删除即可;
2. 被删除节点仅有一个子节点,将其子节点作为其父节点的子节点即可;
3. 被删除节点有两个子节点,需要寻找其前驱或后继来替代被删除节点,并删除前驱或后继节点。
1. 寻找前驱节点
寻找前驱节点是指在二叉排序树中,寻找比被删除节点的值小的最大节点。要做到这一点,我们需要在被删除节点的左子树中,找到值最大的节点,即左子树的最右下角节点。
BiTree findMax(BiTree root) { while(root->right!=NULL) { root=root->right; } return root; }
2.寻找后继节点
寻找后继节点是指在二叉排序树中,寻找比被删除节点的值大的最小节点。要做到这一点,我们需要在被删除节点的右子树中,找到值最小的节点,即右子树的最左下角节点。
BiTree findMin(BiTree root) { while(root->left!=NULL) { root=root->left; } return root; }
3. 删除节点
删除节点需要对被删除节点的子树进行重连,也就是将被删除节点的父节点、子节点与被删除节点割离,然后对两部分子树进行合并。
void deleteNode(BiTree *root,int data) { if((*root)==NULL) { return; } if((*root)->data>data) { deleteNode(&((*root)->left),data); } else if((*root)->dataright),data); } else { if((*root)->left==NULL&&(*root)->right==NULL) { (*root)=NULL; } else if((*root)->left==NULL) { (*root)=(*root)->right; } else if((*root)->right==NULL) { (*root)=(*root)->left; } else { BiTree max=findMax((*root)->left); (*root)->data=max->data; deleteNode(&((*root)->left),max->data); } } }
首先比较删除节点的值与当前节点的值的大小关系,递归查找要删除的节点,直到找到该节点;对于待删除节点是否有子节点,分别进行不同的操作。如果没有子节点,则直接删除该节点;如果只有一个子节点,则将该子节点作为当前节点的子节点;如果有两个子节点,则找到该节点的前驱或后继节点来替换此节点,并删除前驱或后继节点。