学习《算法导论》 二叉查找树 总结
来源:互联网 发布:淘宝怎么心极多点 编辑:程序博客网 时间:2024/05/22 03:35
学习《算法导论》 二叉查找树 总结
二叉查找树
二叉查找树可以用链表结构来表示, 其中每个结点除了包括卫星数据外,还包括域left, right, parent; 它们分别指向结点的左儿子,右儿子和父结点.
二叉查找树的链表结构定义如下:
typedef int ElemType;// 二叉树的链表结构typdef struct TreeNode{ ElemType data; struct TreeNode* left; // 指向左儿子 struct TreeNode* right; // 指向右儿子 struct TreeNode* parent; // 指向父结点}TreeNode, *PointTree, *TreeNode;
结点元素数据要满足二叉查找树性质:
1. 设x为二叉查找树的一个结点,若y是x的左子树中的一个结点,则:y.data <= x.data ; 若y是x的右子树中的一个结点,则:x.data <= y.data
注意:这里说的是x的左子树中的一个结点,并不是说x的左结点,右结点类似.
二叉查找树的遍历
根据上面的二叉查找树性质,可以按顺序输出树中的所有结点. 这些算法有:
中序遍历算法:根结点的输出介于左子树和右子树之间
先序遍历算法:根结点的输出介于左子树和右子树之前
后序遍历算法.:根结点的输出介于左子树和右子树之后
中序遍历算法
VOS_VOID INORDED-TREE-WALK(PointTree RootNode){ if (RootNode != NULL) { INORDED-TREE-WALK(RootNode->left); printf(RootNode->data); INORDED-TREE-WALK(RootNode->right); } return;}
先序遍历算法
VOS_VOID PREORDER-TREE-WALK(PointTree TreeRoot){ if (TreeRoot != NULL) { printf(TreeRoot->data); PREORDER-TREE-WALK(TreeRoot->left); PREORDER-TREE-WALK(TreeRoot->right); }}
后序遍历算法
VOS_VOID POSTORDER-TREE-WALK(PointTree TreeRoot){ if (TreeRoot != NULL) { POSTORDER-TREE-WALK(TreeRoot->left); POSTORDER-TREE-WALK(TreeRoot->right); printf(TreeRoot->data); }}
二叉查找树的查询
二叉查找树能支持以下操作:SEARCH, MINIMUM, MAXIMUM, SUCCESSOR和PREDECESSOR等. 并说明对高度为h的树, 它们都可以在O(h)时间内完成.
SEARCH
// 给定一关键字,查找树中是否存在; 递归算法PointTree TREE-SEARCH(PointTree Tree, ElemType KeyElem){ if (Tree == NULL) { return NULL; } if (Tree->data == KeyElem) { return Tree; } else if (KeyElem < Tree->data) { // 递归 return TREE-SEARCH(Tree->left, KeyElem); } else { return TREE-SEARCH(Tree->right, KeyElem); }}
// 非递归算法PointTree ITERATIVE-TREE-SEARCH(PointTree Tree, ELemType KeyElem){ PointTree CurrentTreeNode = Tree; if (NULL == Tree) return NULL; while(CurrentTreeNode->data != KeyElem) { if (KeyElem < CurrentTreeNode->data) CurrentTreeNode = CurrentTreeNode->left; else CurrentTreeNode = CurrentTreeNode->right; } return Tree;}
MINIMUM
PointTree TREE-MINIMUM(PointTree TreeRoot){ PointTree CurrentNode = TreeRoot; while(CurrentNode->left != NULL) { CurrentNode = CurrentNode->left; } return CurrentNode;}
MAXIMUM
PointTree TREE-MAXIMUM(PointTree TreeRoot){ PointTree CurrentNode = TreeRoot; while(CurrentNode->right != NULL) { CurrentNode = CurrentNode->right; } return CurrentNode;
后继
前驱和后继,这两个概念不好理解,在线性表,队列或栈中,前驱就是该结点前面一个结点,后继就是该结点后面一个结点;但是树不同,因为树有三种遍历方法,在前序遍历中结点的前驱和在后序遍历中结点的前驱是不一样的。后继也一样。所以在树结构中,谈到前驱和后继,是在某种遍历方法的前提下说的。
在看后继的算法之前,先看算法导论中的一道题:
题目:若二叉查找树T中某个结点x的右子树为空,且x有一个后继y,则:y就是x的最低祖先,且其左孩子也是x的祖先.
先解释下这个结论吧,结论比较绕口。我这里用集合来表示下吧:
{祖先 | 其左孩子也是x的} 这个集合中离x最近的祖先。
证明:给定结点x,其后继y存在,则:y>x. 由于y大于x,则y不可能在x的左子树中,又因为x的右子树为空。则y只能是结点x的祖先. 或x祖先的右子树中。
又因为y是其中大于x且最小的一个,则y不可能是其祖先的右子树,那么我们可以将范围缩小至y必定为x的祖先
又根据y>x,则x必定在y的左子树中,即y的左孩子也是x的祖先(x也是x的祖先)。
下面是算法
// 查找结点x的后继PointTree TREE-SUCCESSOR(PointTree TreeNode){ // TreeNode的右子树非空,则TreeNode的后继就是 // 右子树中的最小结点 if (TreeNode->right != NULL) { return TREE-MINIMUM(TreeNode->right); } else { // 若右子树为空,则根据上面的题目,即查找TreeNode的最低祖 // 先,且其左孩子也是x的祖先,下面算法实现: // 从TreeNode开始向上查找,直到找到某个结点的左孩子也是 // TreeNode的祖先为止 PointTree CurrentNode = TreeNode->parent; // 条件:CurrentNode->right == TreeNode要求:CurrentNode要大于TreeNode while((CurrentNode != NULL) && (CurrentNode->right == TreeNode)) { TreeNode = CurrentNode; CurrentNode = CurrentNode->parent; } return CurrentNode;}
算法比较难理解,最好画个树的图来理解.
二叉查找树的插入
插入会引起二叉查找树的动态集合的变化,所以插入之后,要修改数据的结构。但要按照二叉查找树的原则来修改。话不多说,直接看算法:
// 将NewNode插入TreeRoot树中VOS_VOID TREE-INSERT(PointTree TreeRoot, TreeNode NewNode){ PointTree CurrentNode = TreeRoot; PointTree TmpNode = NULL; // 若CurrentNode 为空,也即找到NewNode插入的位置 while(CurrentNode != NULL) { TmpNode = CurrentNode; // 左子树 if (NewNode->data < CurrentNode->data) { CurrentNode = CurrentNode->left; } else // 右子树 { CurrentNode = CurrentNode->right; } } // 由上面可知,TmpNode就是NewNode的父结点 NewNode->parent = TmpNode; // 若TmpNode为空,则说明该树是空树 if (NULL == TmpNode) { TreeRoot = NewNode; } else if (NewNode->data < TmpNode->data) { // NewNode放在TmpNode的左子树 TmpNode->left = NewNode; } else { // NewNode放在TmpNode的右子树 TmpNode->right = NewNode; } return;}
二叉查找树的删除
删除操作有三种情况:
1. 若删除的结点z没有子女,即z为叶子结点,则修改其父结点,将它的子女改为NULL;
2. 若z只有一个子女,则通过在其子结点与父结点间建立一条链来删除z;
3. 若z有两个子女,则找到z的后继(后继也就是:z的右子树的最左边的结点,所以它是没有左子女的);
代码如下:
VOS_VOID TREE-DELETE(PointTree TreeRoot, TreeNode DeleteNode){ TreeNode CurrentNode = NULL; TreeNode ChildNode = NULL; // 第一种情况和第二种情况:z最多只有一个子结点 if (DeleteNode->left == NULL || DeleteNode->right != NULL) { CurrentNode = DeleteNode; } else { // 第三种情况:找到z的后继 CurrentNode = TREE-SUCCESSOR(DeleteNode); } // ChildNode被置为CurrentNode的非NULL子女结点 if (CurrentNode->left != NULL) // 即这里是第二种情况或第三种情况 { ChildNode = CurrentNode->left; else // 即这里是第一种情况或第二种情况 { ChildNode = CurrentNode->right; } if(ChildNode != NULL) // 这就是第二种情况 { ChildNode->parent = CurrentNode->parent; } if (CurrentNode->parent == NULL) // CurentNode为DeleteNode,但是无祖先,说明DeleteNode为根结点 { TreeRoot = ChlidNode; } else if (CurrentNode == CurrentNode->parent->left) { CurentNode->parent->left = ChlidNode; } else { CurentNode->parent->right = ChildNode; } if (CurentNode != DeleteNode) { DeleteNode->data = CurentNode->data; } free(CurentNode); return;}
这个删除算法没看懂,有三种情况比较好理解,但是这个算法没看明白。
下次找资料好好看看
下面再介绍一种二叉查找树的删除操作算法,这个比较好理解,实际上就是分别讨论了三种情况:
/*删除一个节点p为树根节点指针node_value要删除的节点的值*/ bool DeleteTreeNode(Tree * p,int node_value) { Tree * t = FindNode(p,node_value); if(t == NULL) { return false; } if((t->left == NULL)&&(t->right == NULL)) {/*没有子节点*/ Tree* tmp = t; if(tmp->parent->left == tmp) { tmp->parent->left = NULL; } else { tmp->parent->right = NULL; } delete tmp; tmp = NULL; } else if((t->left == NULL)||(t->right == NULL)) {/*一个子节点*/ Tree* tmp = t; if(tmp->parent->left == tmp) { tmp->parent->left = (tmp->left ==NULL)?tmp->right :tmp->left; } else { tmp->parent->right = (tmp->left ==NULL)?tmp->right :tmp->left; } delete tmp; tmp = NULL; } else {/*两个子节点*/ Tree* s = Successor(t); if(s == NULL) { return false; } t->node_value = s->node_value ; DeleteTreeNode(s); } }
- 学习《算法导论》 二叉查找树 总结
- 算法导论 二叉查找树
- 《算法导论》学习总结——第三部分3二叉查找树
- 算法导论学习笔记——二叉查找树
- 《算法导论》学习笔记--第十二章 二叉查找树
- 算法导论学习笔记(八):二叉查找树
- 最优二叉查找树详解(算法导论学习笔记)
- 算法导论第十二章:二叉查找树
- 算法导论P151 二叉查找树
- 算法导论(八)二叉查找树
- 二叉查找树 算法导论笔记
- 算法导论 最优二叉查找树
- 二叉查找树 (算法导论12)
- 最优二叉查找树 算法导论216
- 算法导论15.5 最优二叉查找树
- 二叉查找树(来自算法导论)
- 算法导论——二叉查找树
- 算法导论--二叉查找树--C++实现
- 二叉排序树--源码
- JAVA基础之内部类
- CSS 水平垂直居中 方法三
- 二叉树的深度
- 苹果开发 笔记(75)UISegmentedControl
- 学习《算法导论》 二叉查找树 总结
- hdu 2100 lovekey
- ios
- Spring 事务
- iOS 人脸识别
- [心得记录] MongoDB+nodejs(mac 环境)
- Fragment与Activity之间传递数据
- 柔性数组
- 基于c# 类、接口、结构的联系与区别详解