【算法导论】二叉搜索树

什么是二叉搜索树

顾名思义,一棵二叉搜索树是以一棵二叉树来组织的。这样一棵树可以使用一个链表数据结构来表示,其中每个结点就是一个对象。除了key和卫星数据之外,每个结点还包含属性left、right和p,它们分别指向结点的左孩子、右孩子和双亲。如果某个孩子结点和父结点不存在,则相应属性的值为NIL。根结点是树中唯一父指针为NIL的结点。

二叉搜索树中的关键字总是以满足二叉搜索树性质的方式来存储:

设x是二叉搜索树中的一个结点。如果y是x左子树中的一个结点,那么 y.key <= x.key。如果y是x右子树中的一个结点,那么 y.key >= x.key。

二叉搜索树性质允许我们通过一个简单的递归算法来按序输出二叉搜索树中的所有关键字,这种算法称为中序遍历(inorder tree walk)算法。这样命名的原因是输出的子树根的关键字位于其左子树关键字值和右子树的关键字值之间。(类似地,先序遍历(preorder tree walk)中输出的根的关键字值在其左右子树的关键字之前,而后序遍历(postorder tree walk)输出的根的关键字在其左右子树的关键字值之后)。

Inorder-tree-walk(x)
1    if (x != NIL)
2        Inorder-tree-walk(x.left)
3        print x.key
4        Inorder-tree-walk(x.right)

定理:如果x是一棵有n个结点子树的根,那么调用Inorder-tree-walk(x)需要O(n)时间。

证明:设x结点左子树有k个结点且右子树上有n-k-1个结点,则:

T(n) <= T(k) + T(n-k-1) + d, T(0) = c

其中,c,d为常数。

可以使用替换法证明T(n) <= (c+d)n + c,得到T(n) = O(n)。

Exercise 12.1

1.对于关键字集合{1, 4, 5, 10, 16, 17, 21},分别画出高度为2、3、4、5和6的二叉搜索树。

【解答】略。

2.二叉搜索树性质与最小堆性质之间有什么不同?能使用最小堆性质在O(n)时间内按序输出一棵有n个结点树的关键字吗?可以的话,请说明如何做,否则解释理由。

【解答】最小堆只是父结点关键字值小于左右孩子结点关键字值,无法遍历的方法在O(n)的时间内得到有序序列。

而二叉搜索树根结点关键字值大于左子树结点关键字值,小于右子树结点关键字值,可以通过中序遍历得到有序序列。

3.设计一个执行中序遍历的非递归算法。(提示:一种容易的方法是使用栈作为辅助数组结构;另一种较复杂但比较简洁的做法是不使用栈,但要假设能测试两个指针是否相等。)

【解答】参考代码如下:

Inorder-tree-walk(x)
1    let S be a new stack
2    while (!S.empty() || x != NIL)
3        while (x != NIL)
4            S.push(x)
5            x = x.left
6        x = S.top()
7        S.pop()
8        print x.key
9        x = x.right

4.对于一棵有n个结点的树,请设计在O(n)时间内完成的先序遍历算法和后序遍历算法。

【解答】参考代码如下:

先序遍历的递归与非递归算法

Preorder-tree-walk(x)
1    if (x != NIL)
2        print x.key
3        Preorder-tree-walk(x.left)
4        Preorder-tree-walk(x.right)

Preorder-tree-walk(x)
1    let S be a new stack
2    if (x != NIL) S.push(x)
3    while (!S.empty())
4        x = S.top()
5        S.pop()
6        print x.key
7        if (x.right != NIL) S.push(x.right)
8        if (x.left != NIL) S.push(x.left)

后序遍历的递归与非递归算法

Postorder-tree-walk(x)
1    if (x != NIL)
2        Postorder-tree-walk(x.left)
3        Postorder-tree-walk(x.right)
4        print x.key

Postorder-tree-walk(x)
1    let S be a new stack
2    r = NIL
3    while (!S.empty() || x != NIL)
4        if (x != NIL)
5            S.push(x)
6            x = x.left
7        else
8            x = S.top()
9            if (x.right != NIL && x.right != r)
10                x = x.right
11                S.push(x)
12                x = x.left
13            else
14                x = S.top()
15                S.pop()
16                print x.key
17                r = x
18                x = NIL

5.因为在基于比较的排序模型中,完成n个元素的排序,其最坏情况下需要Ω(nlgn)时间。试证明:任何基于比较的算法从n个元素的任意序列中构造一棵二叉搜索树,其最坏情况下需要Ω(nlgn)的时间。

【证明】构造二叉搜索树时间T(n),中序遍历二叉树时间C(n)=O(n)

先构造一棵二叉搜索树,然后再中序遍历得到有序序列;这个过程是一个比较排序过程,可知其最坏情况下需要Ω(nlgn)时间。

得到 T(n) + C(n) = Ω(nlgn),因此 T(n) = Ω(nlgn) - C(n) = Ω(nlgn)。

查询二叉搜索树

(1) 查找

递归算法

Tree-search(x, k)
1    if (x == NIL || k == x.key)
2        return x
3    if (k < x.key)
4        return Tree-search(x.left, k)
5    else return Tree-search(x.right, k)

非递归算法

Iterative-tree-search(x, k)
1    while (x != NIL && x.key != k)
2        if (k < x.key)
3            x = x.left
4        else x = x.right
5    return x

(2) 最小关键字元素和最大关键字元素

最小关键字元素

Tree-minimum(x)
1    while (x.left != NIL)
2        x = x.left
3    return x

最大关键字元素

Tree-maximum(x)
1    while (x.right != NIL)
2        x = x.right
3    return x

(3) 后继和前驱

如果所有的关键字互不相同,则一个结点x的后继是大于x.key的最小关键字结点。

如果x的右子树非空,那么x的后继恰是x右子树中最左结点;如果x的右子树为空并有一个后继y,那么y就是x的有左孩子的最底层祖先,并且它也是x的一个祖先。

Tree-successor(x)
1    if (x.right != NIL)
2        return Tree-minimum(x.right)
3    y = x.p
4    while (y != NIL && x == y.right)
5        x = y
6        y = y.p
7    return y

过程Tree-predecessor与Tree-successor是对称的

Tree-predecessor(x)
1    if (x.left != NIL)
2        return Tree-maximum(x.left)
3    y = x.p
4    while (y != NIL && x == y.left)
5        x = y
6        y = y.p
7    return y

定理:在一棵高度为h的二叉搜索树上,动态集合上的操作Search、Minimum、Maximum、Successor和Predecessor可以在O(h)时间内完成。

Exercise 12.2

1.假设一棵二叉搜索树中的结点在1到1000之间,现在想查找数值为363的结点。下面序列中哪个不是查找过的序列?

   a. 2, 252, 401, 398, 330, 344, 397, 363

   b. 924, 220, 911, 244, 898, 258, 362, 363

   c. 925, 202, 911, 240, 912, 245, 363

   d. 2, 399, 387, 219, 266, 382, 381, 278, 363

   e. 935, 278, 347, 621, 299, 392, 358, 363

【解答】c,e 均不可能为查找序列。

2.写出Tree-minimum和Tree-maximum的递归版本。

【解答】参考代码如下所示:

Tree-minimum(x)
1    if (x.left == NIL)
2        return x
3    return Tree-minimum(x.left)

Tree-maximum(x)
1    if (x.right == NIL)
2        return x
3    return Tree-maximum(x.right)

3.写出过程Tree-predecessor的伪代码。

【解答】见如上给出的伪代码。

4.Bunyan教授认为他发现了一个二叉搜索树的重要性质。假设在一棵二叉搜索树中查找一个关键字为k,查找结束于一个树叶。考虑三种集合:A为查找路径左边的关键字集合;B为查找路径上的关键字集合;C为查找路径右边的关键字集合。Bunyan教授声称:任何a,b,c分别以属于A,B,C,一定满足 a ≤ b ≤ c。请给出该教授这个论断的最小可能的反例。

【解答】如下图所示,查找路径为18->14->10,而15<18。

5.证明:如果一棵二叉搜索树中的一个结点有两个孩子,那么它的后继没有左孩子,它的前驱没有右孩子。

【证明】如果x有两个孩子,则后继为Tree-minimum(x.left),则后继肯定没有左孩子;

前驱为Tree-maximum(x.right),则必然没有右孩子。

6.考虑一棵二叉搜索树T,其关键字互不相同。证明:如果T中一个结点x的右子树为空,且x有一个后继y,那么y一定是x的最底层祖先,并且其左孩子也是x的祖先。(注意到,每个结点都是它自己的祖先。)

【证明】参考Tree-successor算法。即为循环部分。

7.对于一棵有n个结点的二叉搜索树,有另一种方法来实现中序遍历,先调用Tree-minimum找到这棵树的最小元素,然后再调用n-1次的Tree-successor。证明:该算法的运行时间为O(n)。

【证明】依次找后继结点,相当于树中每条边都遍历两次,总时间O(n)。

8.证明:在一棵高度为h的二叉搜索树中,不论从哪个结点开始,k次连续的Tree-successor调用所需的时间为O(k+h)。

【证明】

9.设T是一棵二叉搜索树,其关键字互不相同;设x是一个叶结点,y为其父结点。证明:y.key或者是T树中大于x.key的最小关键字,或者是T树中小于x.key的最大关键字。

【证明】如果x是y的左孩子,则y是x的后继,则y.key是T树中大于x.key的最小关键字。

如果x是y的右孩子,则y是x的前驱,则y.key是T树中小于x.key的最大关键字。

插入和删除

(1) 插入

根将一个新值v插入到一棵二叉搜索树T中,调用Tree-insert过程。该过程以结点z作为输入,其中z.key = v,z.left = z.right = NIL。这个过程要修改T和z的某些属性,来把z插入到树中的相应位置上。

Tree-insert(T, z)
1    y = NIL
2    x = T.root
3    while (x != NIL)
4        y = x
5        if (z.key < x.key)
6            x = x.left
7        else x = x.right
8    z.p = y
9    if (y == NIL)
10        T.root = z  // tree T is empty
11    else if (z.key < y.key)
12        y.left = z
13    else y.right = z

(2) 删除

从一棵二叉搜索树T中删除一个结点z的整个策略分为如下三种基本情况:

  1. 如果z没有孩子结点,那么只是简单地将它删除,并修改它的父结点,用NIL作为孩子来替换z;
  2. 如果z只有一个孩子,那么将这个孩子提升到树中z的位置上,并修改z的父结点,用z的孩子来替换z;
  3. 如果z有两个孩子,那么找z的后继y(一定在z的右子树中),并让y占据树中z的位置。这个后继y位于z的右子树中并且没有左孩子(见练习12.2-5)。这时又分两种情况:如果y是z的右孩子,那么用y替换z,并仅留下y的右孩子;否则,y位于z的右子树中但并不是z的右孩子,这种情况下,先用y的右孩子替换y,然后再用y替换z。

为了在二叉搜索树内移动子树,定义一个子过程Transplant,它是用另一棵子树替换一棵子树并成为其双亲的孩子结点。当Transplant用一棵以v为根的子树来替换一棵以u为根的子树时,结点u的双亲就变为结点v的双亲,并且最后v成为u的双亲的相应孩子。

Tansplant(T, u, v)
1    if (u.p == NIL)
2        T.root = v
3    else if (u == u.p.left)
4        u.p.left = v
5    else u.p.right = v
6    if (v != NIL)
7        v.p = u.p

利用Transplant过程,下面是从二叉搜索树T中删除结点z的删除过程:

Tree-delete(T, z)
1    if (z.left == NIL)
2        Transplant(T, z, z.right)
3    else if (z.right == NIL)
4        Transplant(T, z, z.left)
5    else y = Tree-minimum(z.right)
6        if (y.p != z)
7            Transplant(T, y, y.right)
8            y.right = z.right
9            y.right.p = y
10        Transplant(T, z, y)
11        y.left = z.left
12        y.left.p = y

定理:在一棵高度为h的二叉搜索树上,实现动态集合损伤Insert和Delete的运行时间均为O(h)。

Exercise 12.3

1.给出Tree-insert过程的一个递归版本。

2.假设通过反复向一棵树中插入互不相同的关键字来构造一棵二叉搜索树。证明:在这棵树中查找关键字所检查过的结点数目等于先前插入这个关键字所检查的结点数目加1。

【证明】插入过程与查找过程除了最后一个结点,比较的结点路径是相同的;插入时最后一个结点为NIL,查找过程最后一个是目标结点。

3.对于给定的n个数的集合,可以通过先构造包含这些数据的一棵二叉搜索树(反复使用Tree-insert逐个插入这些数),然后按中序遍历输出这些数的方法,来对它们排序。这个排序算法的最坏情况运行时间和最好情况运行时间各是多少?

【解答】最坏情况是:当集合有序时,用时O(n2)。最好情况为O(nlgn)。

4.删除操作可交换吗?可交换的含义是,先删除x再删除y留下的结果与先删除y再删除x留下的结果树完全一样。如果是,说明为什么;否则,给出一个反例。

【解答】明显是不可交换的。

5.假设为每个结点换一种设计,属性x.p指向x的双亲,属性x.succ指向x的后继。试给出使用这种表示法的二叉搜索树T上Search、Insert和Delete损伤的伪代码。这些伪代码应在O(h)时间内执行完,其中h为树T的高度。(提示:应该设计一个返回某个结点的双亲的子过程。)

6.当Tree-delete中的结点z有两个孩子时,应该选择结点y作为它的前驱,而不是作为它的后继。如果这样做,对Tree-delete应该做些什么必要的修改?一些人提出了一个公平策略,为前驱和后继赋予相等的优先级,这样得到了较好的实验性能。如何对Tree-delete进行修改来实现这样一种公平策略?

附录:二叉搜索树代码示例

/**
 * 如果这段代码好用,那么它是xiaoxxmu写的
 * 否则,我也不知道是谁写的
 */
#include <stdio.h>
#include <stdlib.h>

// 定义结构体表示Binary Search Tree的结点
typedef struct BSTNode
{
    int key; // 键值
    struct BSTNode *left; // 指向左孩子的指针
    struct BSTNode *right; // 指向右孩子的指针
    struct BSTNode *p; // 指向双亲结点的指针
} BSTNode; 

// 用一个数组来创建一棵BST
BSTNode *bst_create(int arr[], int length);
// 插入结点z到以root为根结点的BST中
void bst_node_insert(BSTNode *root, BSTNode *z);
// 插入值为key的结点到以root为根结点的BST中
void bst_key_insert(BSTNode *root, int key);
// 删除过程的辅助过程,用一棵以v为根的子树来替换一棵以u为根的子树
// 替换后,u的双亲为结点v的双亲,v成为u的双亲的相应孩子
void transplant(BSTNode **root, BSTNode *u, BSTNode *v);
// 删除结点z
void bst_delete(BSTNode **root, BSTNode *z);
// 在BST中查找值为key的结点
BSTNode *bst_search(BSTNode *root, int key);
// 查找BST中结点值最小的结点
BSTNode *bst_minimum(BSTNode *root);
// 查找BST中结点值最大的结点
BSTNode *bst_maximum(BSTNode *root);
// 查找结点node的后继结点
BSTNode *bst_successor(BSTNode *node);
// 先序遍历BST
void preorder_tree_walk(BSTNode *root);
// 中序遍历BST
void preorder_tree_walk(BSTNode *root);
// 后序遍历BST
void postorder_tree_walk(BSTNode *root); 

// 用一个数组来创建一棵BST
BSTNode *bst_create(int arr[], int length)
{
    if (length == 0) return NULL;
    BSTNode *root = (BSTNode *) malloc (sizeof(BSTNode));
    root->key = arr[0];
    root->left = root->right = root->p = NULL;
    for (int i = 1; i < length; i++)
    {
        BSTNode *tmp = (BSTNode *) malloc (sizeof(BSTNode));
        tmp->key = arr[i];
        tmp->left = tmp->right = tmp->p = NULL;
        bst_node_insert(root, tmp);
    }
    return root;
}

// 插入结点z到以root为根结点的BST中
void bst_node_insert(BSTNode *root, BSTNode *z)
{
    BSTNode *y = NULL, *x = root;
    while (x != NULL)
    {
        y = x;
        if (z->key < x->key) x = x->left;
        else x = x->right;
    }
    z->p = y;
    if (y == NULL) root = z;
    else if (z->key < y->key) y->left = z;
    else y->right = z;
}

// 插入值为key的结点到以root为根结点的BST中
void bst_key_insert(BSTNode *root, int key)
{
    BSTNode *tmp = (BSTNode *) malloc (sizeof(BSTNode));
    tmp->key = key;
    tmp->left = tmp->right = tmp->p = NULL;
    bst_node_insert(root, tmp);
}

// 删除过程的辅助过程,用一棵以v为根的子树来替换一棵以u为根的子树
// 替换后,u的双亲为结点v的双亲,v成为u的双亲的相应孩子
void transplant(BSTNode **root, BSTNode *u, BSTNode *v)
{
    if (u->p == NULL) *root = v;
    else if (u == u->p->left) u->p->left = v;
    else u->p->right = v;
    if (v != NULL)
        v->p = u->p;
}

// 删除结点z
void bst_delete(BSTNode **root, BSTNode *z)
{
    if (z->left == NULL) transplant(root, z, z->right);
    else if (z->right == NULL) transplant(root, z, z->left);
    else
    {
        BSTNode *y = bst_minimum(z->right);
        if (y->p != z)
        {
            transplant(root, y, y->right);
            y->right = z->right;
            y->right->p = y;
        }
        transplant(root, z, y);
        y->left = z->left;
        y->left->p = y;
    }
}

// 在BST中查找值为key的结点
BSTNode *bst_search(BSTNode *root, int key)
{
    if (root == NULL || root->key == key)
        return root;
    if (key < root->key)
        return bst_search(root->left, key);
    else return bst_search(root->right, key);
}

// 查找BST中结点值最小的结点
BSTNode *bst_minimum(BSTNode *root)
{
    while (root->left != NULL)
        root = root->left;
    return root;
}

// 查找BST中结点值最大的结点
BSTNode *bst_maximum(BSTNode *root)
{
    while (root->right != NULL)
        root = root->right;
    return root;
}

// 查找结点node的后继结点
BSTNode *bst_successor(BSTNode *node)
{
    if (node->right != NULL)
        return bst_minimum(node->right);
    BSTNode *y = node->p;
    while (y != NULL && node == y->right)
    {
        node = y;
        y = y->p;
    }
    return y;
}

// 先序遍历BST
void preorder_tree_walk(BSTNode *root)
{
    if (root)
    {
        printf("%d ", root->key);
        preorder_tree_walk(root->left);
        preorder_tree_walk(root->right);
    }
}

// 中序遍历BST
void inorder_tree_walk(BSTNode *root)
{
    if (root)
    {
        inorder_tree_walk(root->left);
        printf("%d ", root->key);
        inorder_tree_walk(root->right);
    }
}

// 后序遍历BST
void postorder_tree_walk(BSTNode *root)
{
    if (root)
    {
        postorder_tree_walk(root->left);
        postorder_tree_walk(root->right);
        printf("%d ", root->key);
    }
}

int main(int argc, char *argv[])
{
    int arr[] = {6, 3, 8, 5, 7, 4, 9, 1, 2};
    int length = (sizeof(arr)) / (sizeof(arr[0]));
    BSTNode *root = bst_create(arr, length);
    bst_key_insert(root, 10);
    preorder_tree_walk(root);
    printf("\n");
    inorder_tree_walk(root);
    printf("\n");
    postorder_tree_walk(root);
    printf("\n"); 

    BSTNode *node8 = bst_search(root, 8);
    if (node8 == NULL)
        printf("8 is not in the binary search tree!\n");
    else printf("8 is in the binary search tree!\n");
    BSTNode *node11 = bst_search(root, 11);
    if (node11 == NULL)
        printf("11 is not in the binary search tree!\n");
    else printf("11 is in the binary search tree!\n"); 

    BSTNode *minNode = bst_minimum(root);
    printf("%d\n", minNode->key);
    BSTNode *maxNode = bst_maximum(root);
    printf("%d\n", maxNode->key); 

    BSTNode *sucNode8 = bst_successor(node8);
    printf("%d\n", sucNode8->key);
    BSTNode *node2 = bst_search(root, 2);
    BSTNode *sucNode2 = bst_successor(node2);
    printf("%d\n", sucNode2->key); 

    bst_delete(&root, root);
    preorder_tree_walk(root);
    printf("\n");
    inorder_tree_walk(root);
    printf("\n");
    postorder_tree_walk(root);
    printf("\n"); 

    bst_delete(&root, node8);
    preorder_tree_walk(root);
    printf("\n");
    inorder_tree_walk(root);
    printf("\n");
    postorder_tree_walk(root);
    printf("\n"); 

    return 0;
}
时间: 2024-10-12 13:32:56

【算法导论】二叉搜索树的相关文章

算法导论—二叉搜索树(BST)

华电北风吹 天津大学认知计算与应用重点实验室 日期:2015/9/9 与散列表一样,搜索树数据结构也支持动态集合操作,包含插入,查询,删除,最小值,最大值,前驱,后继等. 一.二叉搜索树: 二叉搜索树节点:关键字key,卫星数据,左孩子指针,右孩子指针,父节点指针,其他特殊类型(红黑树的节点颜色,AVL树的树高等). 二叉搜索树性质:x是二叉搜索树中的任意一个节点.若y是x左子树中任意一个节点有x.key>=y.key.若y是x右子树中任意一个节点有x.key<=y.key. 二.二叉搜索树的

算法导论-----------二叉搜索树

先上二叉树查找树的删除的代码,因为删除是二叉查找树最复杂的操作: int BinarySearchTree<T>::tree_remove(const T& elem) { BinarySearchTreeNode<T> *z = tree_search(elem);//根据元素查找到要删除的节点 BinarySearchTreeNode<T> *x, *y; if (z != NULL) { //用y来表示实际要删除的节点 if (z->left ==

70 数组的Kmin算法和二叉搜索树的Kmin算法对比

[本文链接] http://www.cnblogs.com/hellogiser/p/kmin-of-array-vs-kmin-of-bst.html [分析] 数组的Kmin算法和二叉搜索树的Kmin算法非常类似,其本质是找序列中的第K大或者第K小的元素,可以借鉴QuickSort的思想加以实现. [Kmin_of_Array] C++ Code 1234567891011121314151617181920212223242526272829303132333435363738394041

算法导论-二叉查找数

目录 引言 二叉查找树 节点定义 查找操作 插入操作 删除操作 二叉查找树存在问题 完整源码 讨论区 参考资料 内容                             1.引言                                   前面的文章介绍过二分查找.散列表查找:二分查找效率为Θ(lgn).二分查找要求数组是静态的,即元素不能动态的插入和删除,否则会耗费较多的时间:散列表查找效率可以到达Θ(1),但是一般用于“等于性查找“,不能进行“范围查找”;本文介绍的二叉查找树,(

数据结构与算法之二叉搜索树

与链表不同,树是一种非线性的数据结构.树中最常用的是二叉树,二叉树限制了子树的数量,也就是每个结点的子树至多2个,并且这两个子树是有顺序的.而二叉搜索树(二叉查找树,二叉排序树)是指根节点的关键字大于左子树的,而小于右子树,并且,左右子树也是一颗二叉搜索树.也就是说中序遍历一颗二叉搜索树,它的输出是从小到大排序好的. 除了普通的二叉搜索树之外,还有很多关于它的变形. 二叉平衡搜索树,即即是一颗二叉平衡树,也是一颗搜索树,平衡树即任意一个结点的左子树的高度与右子树的高度之差的绝对值不大于1. 红黑

C++算法之 二叉搜索树转换为双向链表

题目: 输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的节点,只能调整树中节点指针的方向: 分析: 1:由于要求链表是有序的,可以借助二叉树中序遍历,因为中序遍历算法的特点就是从小到大访问结点.当遍历访问到根结点时,假设根结点的左侧已经处理好,只需将根结点与上次访问的最近结点(左子树中最大值结点)的指针连接好即可.进而更新当前链表的最后一个结点指针. 2:由于中序遍历过程正好是转换成链表的过程,即可采用递归处理 代码: // BT.cpp : 定义控制台应用程序的

一步两步学算法之二叉搜索树

Binary Search Tree  又叫二叉查找树,二叉排序树 这是种什么样的树呢? 其实就是根节点的左子树比根节点小  右子树比根节点大  同时 左子树和右子树也是二叉搜索树 代码比较简单 基本用递归实现 比较好理解  只有删除带有左右子树的节点时比较难理解 方法就是 直接在右子树找一个最小的节点 取代要被删除的节点 再继续删除右子树里的节点 详细看代码 1 #include "stdio.h" 2 #include "stdlib.h" 3 typedef

【算法设计-二叉搜索树】二叉查找树的操作与实现

二叉查找树某个结点的左子树的值都比它小,其右子树的值比它大. 要实现的主要操作 代码实现 #include <iostream> using namespace std; // BST的结点 typedef struct node { int key; struct node *lChild, *rChild,*parent; }Node, *BST; BST lvis=NULL;//用来保存父节点的地址 void createBST(BST &p); void assignmentP

数据结构与算法问题 二叉搜索树

1.序 具体实现了二叉查找树的各种操作:插入结点.构造二叉树.删除结点.查找.  查找最大值.查找最小值.查找指定结点的前驱和后继 2.二叉查找树简单介绍 它或者是一棵空树:或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上全部结点的值均小于它的根结点的值. (2)若右子树不空.则右子树上全部结点的值均大于它的根结点的值: (3)左.右子树也分别为二叉排序树 3.二叉查找树的各种操作 此处给出代码.凝视很具体.具体操作请參考代码: #include <iostream> using

hdu 3999 The order of a Tree (二叉搜索树)

1 /****************************************************************** 2 题目: The order of a Tree(hdu 3999) 3 链接: http://acm.hdu.edu.cn/showproblem.php?pid=3999 4 题意: 给你一个序列建立一棵二叉搜索树 要你找出另外一个序 5 列,可以建立和原序列建立的二叉搜索树一样且这个序列 6 是字典序最小 7 算法: 二叉搜索树 8 思想: 对于一个