二叉搜索树的详解(算法导论读书笔记)

来源:互联网 发布:专业java培训 编辑:程序博客网 时间:2024/06/16 14:55

什么是二叉搜索树?

一棵二叉树的节点x,如果y是x左子树中的一个节点,那么y.key<x.key.如果y是x右子树中的一个节点,那么y.key>x.key

二叉树的数据结构

typedef int data_t;typedef struct BST{data_t  data;struct BST *left;struct BST *right;struct BST *parent;}BST;

二叉搜索树的遍历

我们可以通过一个简单的递归算法按序输出二叉搜索树中的所有关键字,这种算法称为中序遍历,类似的,先序遍历输出的根的关键字在其左右子树的关键字之间,后续遍历输出根的值在其左右子树的值之后

以下为二叉树的中序遍历的递归算法与非递归算法

void bst_nonrecur_inorder(BST *root)//use stack to store tree's node {stack<BST *> s_bst;while(s_bst.size() || root!=NULL){if(root!=NULL){s_bst.push(root);root = root->left;}else{root = s_bst.top();s_bst.pop();cout << root->data << " ";root = root->right;}}}void bst_inorder(BST *root){if(root==NULL)return ;else{bst_inorder(root->left);cout << root->data << " ";bst_inorder(root->right);}}
注意:如果x是一棵n个结点字树的根,那么遍历该树需要O(n)时间

二叉树的查询

我们经常需要查找一个存储在二叉搜索树中的关键字。除了search操作之外,还支持诸如minnum,maxinum,successor(求后继节点),predecessor(前驱结点)的查询操作

树的查找

输入指向树根节点的指针和一个关键字k,如果这个检点存在,bst_search返回一个指向关键字k的节点的指针,否则返回NULL
下面为树查找的递归实现与迭代实现
//search a node in the binary-search-treeBST *bst_search(BST *root, data_t data){if(root==NULL || root->data== data)return root;if(data<root->data)return bst_search(root->left, data);elsereturn bst_search(root->right, data);}BST *bst_iterative_search(BST *root, data_t data){if(root==NULL || root->data == data)return root;while(root!=NULL && data!=root->data){if(data<root->data)root = root->left;else if(data>root->data)root = root->right;}return root;}

最大关键字元素和最小关键字元素

从树根开始沿着left孩子指针,直到遇到一个一个节点的left指针为NULL,那么该节点的值就是该二叉搜索树的最小关键字。同理,从树根沿着right孩子指针,直到遇到一个节点的right指针为NULL为止,那么该节点的值就是该二叉搜索树的最大关键字
以下为实现代码
//return the minnest node of treeBST *bst_mininum(BST *root){if(root == NULL)return NULL;while(root->left!=NULL)root = root->left;return root;}//return the maxest node of treeBST *bst_maxinum(BST *root){if(root == NULL)return NULL;while(root->right!=NULL)root = root->right;return root;}
注,以上两个算法的时间复杂度都是O(logn) (n为输得节点的个数)

 节点的前驱和后继

给定一棵二叉搜索树的一个节点,有时候需要按中序遍历的次序查找它的后继。如果所有的关键字互不相同,则一个节点x的后继是大于x.key的最小关键字的节点。
查找前驱过程分为两部分:1. 如果x的右子树非空,那么x的后继则是x右子树的最左节点;2. 如果x的右子树为空且有一个后继节点y,那么y就是最底层祖先并且其左孩子也是一个祖先。为了找到y,只需从x开始沿树而上知道遇到一个其双亲有左孩子的节点
查找节点的前驱则先判断x节点的左子树是否为空,非空则前驱就是左子树的最右节点,空则从x沿树而上直到遇到一个其双亲都有右孩子的节点,那么该节点就是x的前驱结点
下面为实现代码
BST *bst_successor(BST *node){if(node == NULL)return NULL;if(node->right!=NULL)//find successor in the leftest of the right subtreereturn bst_mininum(node->right);//else find it a node leftSubTree from his parentBST *y = node->parent;while(y!=NULL && node==y->right){node = y;y = y->parent;}return y;}BST *bst_predecessor(BST *node){if(node == NULL)return NULL;if(node->left!=NULL)return bst_maxinum(node->left);BST *y = node->parent;while(y!=NULL && node==y->left){node = y;y = y->parent;}return y;}

树的插入与删除

  树的插入与删除操作会引起二叉搜索树表示的动态集合的变化,一定要修改数据结构来反映这个变化,但修改要保持二叉树搜索树的性质的成立

  插入

要将一个新值插入到一棵二叉搜索树T中,需要调用bst_insert。该函数以关键字v作为输入,在函数构造节点node,并将node->left=NULL,node->right=NULL
插入过程为:从树根开始,指针node记录一条向下的简单路锦,并查找要替换新节点insert的NULL,该遍历保持tmo作为node的双亲 
//insert a node into a binary-search-treeBST *bst_insert(BST *root, data_t data){BST *insert = NULL, *node = NULL, *tmp = NULL;insert = bst_newnode(data);  //make a node to insert  node = root;while(node)    //find pos to insert{tmp = node;if(data < node->data)node = node->left;elsenode = node->right;}insert->parent = tmp;if(tmp==NULL)    //tree root is emptyroot = insert;else {if(data < tmp->data) tmp->left = insert;elsetmp->right = insert;}return root;}

树的删除

从一个二叉搜索树T中删除一个节点z,这个过程取决与指向root和待删除节点node,以下为该过程的四种情况
 1. 如果node没有左孩子,那么有其右孩子来替换node,这个右孩子可以是NULL,也可以不是
 2. 如果node仅有一个节点且该节点是左孩子,那么用其左孩子来替换node
 3. 否则node既有一个左孩子又有一个右孩子,我们要查找node的后继y,这个后继位于node的右子树中并且没有左孩子,现在需要将y移除原来的位置进行拼接,并替换树中的node
 3.1. 如果y是z的node的右孩子,那么用y替换node
 3.2 否则,y位于z的右子树中但并不是z的右孩子,这种情况下,先用y的右孩子替换y,然后用y替换node(最后步骤同3.1)
 为了在二叉搜索树内移动子树,定义一个函数bst_transplant,它用凉意可子树替换一棵字数并成为双亲的孩子节点。以下实现为用一棵以v为根的子树来替换一棵以u为根的子树,节点u的双亲就变为节点v的双亲,并且最后v成为u的双亲的相应孩子

//replace subtree of u with subtree of vBST *bst_transplant(BST *root, BST *u, BST *v){if(u->parent==NULL)   //u is root root = v;else if(u->parent->right == u)  // u is his parent's right subtreeu->parent->right = v;elseu->parent->left = v;   //u is his parent's left subtreeif(v!=NULL)   //set v's parentv->parent = u->parent;return root;}
树的删除代码如下
BST *bst_delete(BST *root, data_t data){BST *node = NULL, *y = NULL;node = bst_search(root, data);  //find the deleted nodeif(node==NULL)return NULL;if(node->left == NULL) //case 1root = bst_transplant(root, node, node->right);else if(node->right == NULL)//case 2root = bst_transplant(root, node, node->left);else {y = bst_mininum(node->right);  //find node's successorif(y->parent!=node)         //case 3  follow  make y's parent to node{root = bst_transplant(root, y, y->right);y->right = node->right;node->right->parent = y;}                            //case 3 y->parent == noderoot = bst_transplant(root, node, y);y->left = node->left;y->left->parent = y;}return root;}


原创粉丝点击