二叉搜索树(BST)——基本概念及基本实现代码

二叉搜索树(BST)——基本概念及基本实现代码

1.二叉搜索树概念

二叉搜索树是以一棵二叉树来组织的。每个节点是一个对象,包含的属性有left,right,p和key,其中,left指向该节点的左孩子,right指向该节点的右孩子,p指向该节点的父节点,key是它的值,如果某节点的孩子节点或父节点不存在,则相应属性设置为NULL。根节点是树中唯一一个父节点为NULL的节点。

BST中的关键字有如下特征:
假设x为BST里的一个节点:
(1)如果y为x的左子树里的一个节点,那么y.key<=x.key;
(2)如果y为x的右子树里的一个节点,那么y.key>=x.key.

下面是一个二叉搜索树的例子:
在这里插入图片描述

2.二叉搜索树的几个实现函数

2.1 自定义二叉搜索树的数据结构

//树节点
typedef struct Treenode{
    int key;
    Treenode *p;
    Treenode *left;
    Treenode *right;
    Treenode(int val):key(val),left(NULL),right(NULL),p(NULL) {
    }
};

//树
typedef struct Tree{
    Treenode* root; //指向根节点
};

2.2 查询二叉搜索树

输入一个指向树根的指针和一个关键字k,如果这个节点存在,则返回一个指向关键字为k的节点的指针;否则返回NULL。

Treenode* TREE_SEARCH(Treenode *x, int k) {
   if(x==NULL || x->key==k) {
       return x;
   }
   if(k < x->key) {
       TREE_SEARCH(x->left, k);
   }
   if(k > x->key) {
       TREE_SEARCH(x->right, k);
   }
}

2.3 查找最小关键字元素

要求:
给定一棵树的根节点,返回指向这棵树中最小元素的指针。

思路:
若一个节点X没有左子树,那么由于其右子树里的节点都比它大,因此该节点在以其为根节点的这棵树里是最小的;若节点X有左子树那么由于右子树里的关键字都大于或等于X.key,而左子树里的节点的关键字都小于等于X.key,所以这棵树的最小元素一定在以X.left为根节点的子树里。

Treenode* TREE_MINIMUM(Treenode *x) {
    if(x->left == NULL) {
        return x;
    }
    return TREE_MINIMUM(x->left);
}

2.4 查找最大关键字元素

要求
给定一棵树的根节点,返回指向这棵树中最大元素的指针。

思路:
与查找最小关键字元素的思路一样。

Treenode* TREE_MAXIMUM(Treenode *x) {
    if(x->right == NULL) {
        return x;
    }
    return TREE_MAXIMUM(x->right);
}

2.5 查找后继

要求:
给定一个节点x,若该节点的后继存在,则返回一棵二叉搜索树里的节点x的后继节点;若节点x是该树中的最大关键字节点,则返回NULL。

思路:
一个节点的后继节点是关键字比它大的节点,如果该节点的关键字是树中的最大关键字,则其没有后继;如果不是最大关键字,那它一定有后继节点,这时分2种情况来考虑:
(1)节点x有右子树,那么后继节点就是右子树中关键字最小的节点,可以通过调用TREE_MINIMUM(x->right)获得;
(2)节点x没有右子树,假设其后继节点为y,那么y一定是x的低层祖先,且x在y的左子树里。例如下面这个例子,节点3没有右子树,那就从它的父节点开始,一直找父节点,直到找到一个祖先节点,x在其左子树里,那么这个节点就是其后继节点。
在这里插入图片描述

Treenode* TREE_SUCCESSOR(Treenode *x) {
    if(x->right != NULL) {
        return TREE_MINIMUM(x->right);
    }
    Treenode* p = x->p;
    Treenode* y = x;
    while(p != NULL) {
        if(p->left == y) {
            break;
        }
        y = p;
        p = p->p;
    }
    return p;
}

2.6 查找前驱

查找前驱与查找后继是对称的。

Treenode* TREE_PREDECESSOR(Treenode *x) {
    if(x->left != NULL) {
        return TREE_MAXIMUM(x->left);
    }
    Treenode* p = x->p;
    Treenode* y = x;
    while(p != NULL) {
        if(p->right == y) {
            break;
        }
        y = p;
        p = p->p;
    }
    return p;
}

2.7 插入

要求:
给一颗树及一个新节点的key,将新节点插入到树里的相应位置。

void TREE_INSERT(Tree* t, int insert_number) {
    /*构造新节点*/
    Treenode* new_node = (Treenode*)malloc(sizeof(Treenode));
    new_node->key = insert_number;
    new_node->left = new_node->right = new_node->p = NULL;
    Treenode* temp = t->root;

	//如果是一棵空树
    if(temp == NULL) {
        t->root = new_node;
        return;
    }

    Treenode* temp1;
    while(temp != NULL) {
        temp1 = temp;
        if(temp->key > insert_number) {
            temp = temp->left;
        }
        else if(temp->key < insert_number) {
            temp = temp->right;
        }
    }
    new_node->p = temp1;
    if(new_node->key < temp1->key) {
        temp1->left = new_node;
    }
    else {
        temp1->right = new_node;
    }

}

2.8 删除

要求:
从一棵二叉搜索树T中删除一个给定节点z。

思路:
要分一下几种情况考虑:
(1)z没有左孩子
此时,用z的右孩子来替换z,其右孩子可以是NULL,也可以不是。
在这里插入图片描述
(2)z只有一个孩子,且为左孩子节点
此时用z的左孩子来替换z。
在这里插入图片描述
(3)z既有一个左孩子节点,又有一个右孩子节点
此时应该查找z的后继节点y,并用y来替换z。在上面已经介绍过,后继节点y一定在z的右子树里,且y没有左孩子。
此时又要分2种情况:
1)y是z的右孩子
此时应该用y替换z,并留下y的右子树。
在这里插入图片描述
2)y不是z的右孩子
此时应该先用y的右孩子替换y,然后再用y来替换z。
在这里插入图片描述

实现:
(1)子树移动
在上面的分析中可以看到,实现过程中需要移动子树,因此需要先实现一个函数TREE_TRANSPARENT,该函数实现了用一棵子树来替换另一棵子树并成为其父节点的孩子节点。

void TREE_TRANSPARENT(Tree* t, Treenode* x, Treenode* y) {
    Treenode* p = x->p;
    if(p == NULL) {
        t->root = y;
    }
    if(p != NULL) {
        if(p->left == x) {
            p->left = y;
        }
        else {
            p->right = y;
        }
    }
    if(y != NULL) {
        y->p = x->p;
    }
}

(2)节点删除

void TREE_DELETE(Tree* t, Treenode* x) {
    if(x->left == NULL) {
        TREE_TRANSPARENT(t, x, x->right);
    }
    else if(x->right == NULL) {
        TREE_TRANSPARENT(t, x, x->left);
    }
    else {
        Treenode* temp;
        temp = TREE_MINIMUM(x->right);
        if(x->right != temp) {
            TREE_TRANSPARENT(t, temp, temp->right);
            temp->right = x->right;
            x->right->p = temp;
        }
        TREE_TRANSPARENT(t, x, temp);
        temp->left = x->left;
        x->left->p = temp;
    }
}
  • 11
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
二叉搜索树,也叫二叉查找树(BST,Binary Search Tree),是一种特殊的二叉树。相对于普通的二叉树,它有一个特点:所有左子树的节点都比根节点小,所有右子树的节点都比根节点大。因此,它可以快速地进行查找、插入、删除等操作。 具体来说,二叉搜索树的定义如下: - 节点的左子树所有节点的值都小于该节点的值。 - 节点的右子树所有节点的值都大于该节点的值。 - 左右子树也都是二叉搜索树。 如下图所示,就是一个二叉搜索树的例子: ``` 8 / \ 3 10 / \ \ 1 6 14 / \ / 4 7 13 ``` 在这个树,每个节点都满足左子树的节点值小于该节点的值,右子树的节点值大于该节点的值。比如,节点 3 的左子树是 1 和 6,右子树是 4 和 7,都满足要求。 二叉搜索树的主要操作包括查找、插入和删除。 查找操作可以通过递归或者循环实现。递归实现如下: ``` def search(root, val): if not root or root.val == val: return root if root.val > val: return search(root.left, val) else: return search(root.right, val) ``` 插入操作需要先查找到插入的位置,然后创建一个新节点插入到该位置: ``` def insert(root, val): if not root: return TreeNode(val) if root.val > val: root.left = insert(root.left, val) else: root.right = insert(root.right, val) return root ``` 删除操作比较复杂,需要考虑多种情况。如果要删除的节点只有一个子节点,直接将其子节点替换上来即可。如果要删除的节点有两个子节点,可以找到其右子树的最小节点(或者左子树的最大节点)来替换该节点,然后再删除该最小节点(或者最大节点)。 ``` def delete(root, val): if not root: return None if root.val == val: if not root.left: return root.right elif not root.right: return root.left else: # 找到右子树的最小节点 p = root.right while p.left: p = p.left root.val = p.val root.right = delete(root.right, p.val) elif root.val > val: root.left = delete(root.left, val) else: root.right = delete(root.right, val) return root ``` 需要注意的是,在删除节点时,要保证删除后的树仍然是二叉搜索树

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值