前言
数据结构中,线性表分为无序线性表和有序线性表
无序线性表就是无序,插入和删除没有任何规律,查找时要遍历整棵树,效率低
有序线性表:有较高插入和删除效率,并且具备较高查找效率。因此二叉排序树诞生
定义
二叉排序树又称二叉查找树、二叉搜索树。是一种特殊的二叉树。
若左子树不为空,则二叉树上所有结点的值均小于或等于它根结点的值
若右子树为不空,而二叉树上所有结点的值均岛屿或等于它根结点的值
左右子树分别又是一棵二叉排序树
二叉排序树常用函数
建树
typedef struct node{
int data;
struct node* l;
struct node* r;
struct node* father;
} Node;
typedef struct {
Node* root;
} Tree;
查找
查找指定value值的结点
若查找关键字等于根结点,成功
否则
若小于根结点,查找其左子树
若大于根结点,查找其右子树
同理,在左右子树上类似
Node* SearchBST(Node* node,int value){
if(node==NULL||node->data==value)
return node;
else if(value<node->data)
return SearchBST(node->l,value);
else SearchBST(node->r,value);
}
查找最大结点值
Node* _max(Node* node){
if(node==NULL) return node;
while(node->r!=NULL) node=node->r;
return node;
}
int max_search(Tree* tree){
Node* node=_max(tree->root);
return node->data;
}
printf("%d\n",max_search(&tree));
查找最小结点值
Node* _min(Node* node){
if(node==NULL) return node;
while(node->l!=NULL) node=node->l;
return node;
}
int min_search(Tree* tree){
Node* node=_min(tree->root);
return node->data;
}
printf("%d\n",min_search(&tree));
查找前驱
如果x存在左孩子,x的前驱结点为左子树的最大值
如果x没有左子树:
(1)x是一个右孩子,它的前驱结点为它的父亲结点
(2)x是一个左孩子,查找最低的父亲结点,该父亲结点要具有右孩子,找到的这个最低父亲结点就是x的前驱结点
Node* pre_node(Node* node){
if(node->l!=NULL)
return _max(node->l);
Node* temp=node->father;
while(temp!=NULL&&node==temp->l){
node=temp;
temp=temp->father;
}
return temp;
}
查找后继
如果x存在右孩子,x的后继结点为右子树的最小值
如果x没有右子树:
(1)x是一个左孩子,它的后继结点为它的父亲结点
(2)x是一个右孩子,查找最低的父亲结点,该父亲结点要具有左孩子,找到的这个最低父亲结点就是x的后继结点
Node* post_node(Node* node){
if(node->r!=NULL){
return _min(node->r);
}
Node* temp=node->father;
while(temp!=NULL&&node==temp->r){
node=temp;
temp=temp->father;
}
return temp;
}
插入
若二叉排序树为空,则插入结点为根结点。
否则,继续在左右子树上查找
树中已有,不再插入。
树中没有,查找至某结点左子树或者右子树为空为止。插入应为该结点的左孩子或者右孩子。插入元素一定是叶子结点。
不同插入次序生成不同形态的二叉排序树
void insert(Tree* tree,int value){
Node* node=(Node*)malloc(sizeof(Node));
node->data=value;
node->l=node->r=node->father=NULL;
Node* temp=tree->root;//当前比较结点
if(temp==NULL){
tree->root=node;
return;
}
Node* temp1;
while(temp!=NULL){
temp1=temp;
if(value<temp->data)
temp=temp->l;
else if(value>temp->data)
temp=temp->r;
}
node->father=temp1;
if(node->data<temp1->data)
temp1->l=node;
else temp1->r=node;
}
删除
删除要考虑三种情况:
(1)删除结点为叶子结点
(2)删除结点只有左子树或者只有右子树
(3)删除结点又有左子树又有右子树
相应解决办法:
情况一:其双亲结点指针域的值改为“空”
情况二:其双亲结点的相应指针域的值改为被删除结点的左子树或右子树
情况三:以其前驱(后继)代替,然后再删除该前驱(后继)结点
Node* deleteNode(Node* node,int value){
if(node==NULL)
return NULL;
if(node->data==value){
if(node->l!=NULL&&node->r!=NULL){
Node* temp=node->r;
while(temp->l!=NULL)
temp=temp->l;
node->data=temp->data;
node->r=deleteNode(node->r,temp->data);
}
else{
if(node->l!=NULL){
node->data=node->l->data;
delete(node->l);
}
else if(node->r!=NULL){
node->data=node->r->data;
delete(node->r);
}
else delete(node);
}
return NULL;
}
else if(value>node->data)
node->r=deleteNode(node->r,value);
else if(value<node->data)
node->l=deleteNode(node->l,value);
return node;
}
遍历
遍历与普通二叉树相同。二叉排序树的中序遍历是递增有序
void preorder(Node* node) {
if(node!=NULL){
printf("%d\n",node->data);
preorder(node->l);
preorder(node->r);
}
return;
}
void inorder(Node* node){
if(node!=NULL){
inorder(node->l);
printf("%d\n",node->data);
inorder(node->r);
}
}
void postorder(Node*node){
if(node!=NULL){
postorder(node->l);
postorder(node->r);
printf("%d\n",node->data);
}
}
树的高度
int get_height(Node* node){
if(node==NULL)return 0;
else{
int l_height=get_height(node->l);
int r_height=get_height(node->r);
return max(l_height,r_height)+1;
}
}
思考
斜树:所有结点都只有左子树的二叉树叫左斜树;所有结点都只有右子树的二叉树叫右斜树。
所以平均情况下查找的复杂度为O(lgn)
最坏情况下查找的复杂度为O(n)