二叉树
二叉树是每个结点最多有两个子树的有序树。通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
二叉树有几点重要的性质:
- 性质1:在二叉树的第 i 层上至多有2i-1 个结点。 (i≥1)
- 性质2:深度为 k 的二叉树上至多含2k-1 个结点(k≥1)。
- 性质3:对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为 2 的结点,则必存在关系式:n0 = n2+1。
- 性质4:具有 n 个结点的完全二叉树的深度为log2n+1
- 性质5:若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:
(1) 若 i=1,则该结点是二叉树的根,无双亲,否则,编号为i/2? 的结点为其双亲结点; (2) 若 2i>n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点; (3) 若 2i+1>n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。
采用链式存储结构实现二叉树
链式存储二叉树
1.首先我们要构造可以表示二叉树的节点的结构Binary_node
2.构造类二叉树 Binary_tree,并编写其几个基本的成员函数:
Empty()-检查树是否为空;clear()-将树清空;size()-得到树的大小;leaf_count()-得到叶子数目;height()-得到树高;
以及几个重要的成员函数:
- Binary_tree(constBinary_tree<Entry>&original);拷贝构造成员函数
- Binary_tree&operator=(constBinary_tree<Entry>&original);重载赋值操作符
- ~Binary_tree();析构函数
3.分别编写遍历算法的成员函数
voidinorder(void(*visit)(Entry&));中序遍历(LVR)
- voidpreorder(void(*visit)(Entry&));前序遍历(VLR)
- voidpostorder(void(*visit)(Entry&));后续遍历(LRV)
因为二叉树的性质,三种遍历算法我们都用递归实现,所以分别编写其递归函数
voidrecursive_inorder(Binary_node<Entry>*sub_root,void(*visit)(Entry&));
- voidrecursive_preorder(Binary_node<Entry>*sub_root,153); background-color:inherit; font-weight:bold">void(*visit)(Entry&));
- voidrecursive_postorder(Binary_node<Entry>*sub_root,153); background-color:inherit; font-weight:bold">void(*visit)(Entry&));
4.作为辅助,我们再编写一个print_tree的函数,用以以括号表示法输出 同样使用递归,编写递归函数void recursive_print(Binary_node<Entry>*sub_root); 几个重要的函数代码如下:
template<classEntry>
- voidBinary_tree<Entry>::inorder(void(*visit)(Entry&))
-
-
- {
- recursive_inorder(root,visit);
- }
-
- voidBinary_tree<Entry>::recursive_inorder(Binary_node<Entry>*sub_root,0); background-color:inherit">//Pre:sub_rootiseitherNULLorpointstoasubtreeoftheBinary_tree
- //Post:Thesubtreehasbeentraversedininordersequence
- //Uses:Thefunctionrecursive_inorderrecursively
- {
- if(sub_root!=NULL){
- recursive_inorder(sub_root->left,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> (*visit)(sub_root->data);
- recursive_inorder(sub_root->right,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> }
-
- classEntry>
- voidBinary_tree<Entry>::preorder(void(*visit)(Entry&))
- //Post:Thetreehasbeentraversedinpreordersequence
- //Uses:Thefunctionrecursive_preorder
- recursive_preorder(root,visit);
- voidBinary_tree<Entry>::recursive_preorder(Binary_node<Entry>*sub_root,0); background-color:inherit">//Pre:sub_rootiseitherNULLorpointstoasubtreeoftheBinary_tree
- //Post:Thesubtreehasbeentraversedinpreordersequence
- //Uses:Thefunctionrecursive_preorderrecursively
- if(sub_root!=NULL){
- recursive_preorder(sub_root->left,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> recursive_preorder(sub_root->right,153); background-color:inherit; font-weight:bold">voidBinary_tree<Entry>::postorder(//Post:Thetreehasbeentraversedinpostordersequence
- //Uses:Thefunctionrecursive_postorder
- recursive_postorder(root,153); background-color:inherit; font-weight:bold">voidBinary_tree<Entry>::recursive_postorder(Binary_node<Entry>*sub_root,0); background-color:inherit">//Pre:sub_rootiseitherNULLorpointstoasubtreefotheBinary_tree
- //Post:Thesubtreehasbeentraversedinpostordersequence
- //Uses:Thefunctionrecursive_postorderrecursively
- recursive_postorder(sub_root->left,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> recursive_postorder(sub_root->right,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> (*visit)(sub_root->data);
- voidBinary_tree<Entry>::print_tree()
- recursive_print(root);
- cout<<endl;
- voidBinary_tree<Entry>::recursive_print(Binary_node<Entry>*sub_root)
- cout<<sub_root->data;
- cout<<"(";
- recursive_print(sub_root->left);
- cout<<",";
- recursive_print(sub_root->right);
- cout<<")";
- //其他函数见源码
程序结果
插入二叉树并实现中序、前序和后序遍历
AVL树
AVL树得名于其发明者G.M.Adelson-Velsky和E.M.Landis。AVL树是一个各结点具有平衡高度的扩展的二叉搜索树。在AVL树中,任一结点的两个子树的高度差最多为1,AVL树的高度不会超过1,AVL树既有二叉搜索树的搜索效率又可以避免二叉搜索树的最坏情况(退化树)出现。
AVL树的表示与二叉搜索树类似,其操作基本相同,但插入和删除方法除外,因为它们必须不断监控结点的左右子树的相对高度,这也正是AVL树的优势所在。
实现AVL树的相关运算
1、首先我们修改结构Binary_node,增加Balance_factor用以表示节点平衡情况
2、从二叉搜索树中派生出AVL树,编写其关键的插入和删除成员函数。
Error_codeinsert(constRecord&new_data);
- Error_coderemove(constRecord&old_data);
3、入和删除函数我们都用递归实现 编写递归函数:
Error_codeavl_insert(Binary_node<Record>*&sub_root,
- constRecord&new_data,bool&taller);
- Error_codeavl_remove(Binary_node<Record>*&sub_root,
- constRecord&target,87); background-color:inherit; font-weight:bold">bool&shorter);
以及几个重要的调用函数: 左旋右旋函数:
voidrotate_left(Binary_node<Record>*&sub_root);
- voidrotate_right(Binary_node<Record>*&sub_root);
两次旋转的左右平衡函数
voidright_balance(Binary_node<Record>*&sub_root);
- voidleft_balance(Binary_node<Record>*&sub_root);
删除函数还要分别编写删除左树和删除右树的递归函数
Error_codeavl_remove_right(Binary_node<Record>&sub_root,87); background-color:inherit; font-weight:bold">bool&shorter);
- Error_codeavl_remove_left(Binary_node<Record>*&sub_root,51); font-family:Arial; font-size:14.399999618530273px; line-height:26px"> 4、个重要的成员函数代码如下:
class Record>
- Error_codeAVL_tree<Record>::insert(constRecord&new_data)
- //Post:Ifthekeyofnew_dataisalreadyintheAVL_tree,acodeofduplicate_error
- //isreturned.Otherwise,acodeofsuccessisreturnedandtheRecord
- //new_dataisinsertedintothetreeinsuchawaythatthepropertiesofan
- //AVLtreearepreserved.
- booltaller;
- returnavl_insert(root,new_data,taller);
- classRecord>
- Error_codeAVL_tree<Record>::avl_insert(Binary_node<Record>*&sub_root,bool&taller)
- //Pre:sub_rootiseitherNULLorpointstoasubtreeoftheAVLtree
- //Post:Ifthekeyofnew_dataisalreadyinthesubtree,0); background-color:inherit">//new_dataisinsertedintothesubtreeinsuchawaythatthepropertiesof
- //anAVLtreehavebeenpreserved.Ifthesubtreeisincreaseinheight,the
- //parametertallerissettotrue;otherwiseitissettofalse
- //Uses:MethodsofstructAVL_node;functionsavl_insertrecursively,left_balance,andright_balance
- Error_coderesult=success;
- if(sub_root==NULL){
- sub_root=newBinary_node<Record>(new_data);
- taller=true;
- elseif(new_data==sub_root->data){
- result=duplicate_error;
- false;
- if(new_data<sub_root->data){
- result=avl_insert(sub_root->left,taller);
- if(taller==true)
- switch(sub_root->get_balance()){
- caseleft_higher:
- left_balance(sub_root);
- break;
- caseequal_height:
- sub_root->set_balance(left_higher);
- break;
- caseright_higher:
- sub_root->set_balance(equal_height);
- taller=false;
- else{
- result=avl_insert(sub_root->right,taller);
- true)
- switch(sub_root->get_balance()){
- caseleft_higher:
- caseequal_height:
- sub_root->set_balance(right_higher);
- caseright_higher:
- right_balance(sub_root);
- false;
- returnresult;
- voidAVL_tree<Record>::right_balance(Binary_node<Record>*&sub_root)
- //Pre:sub_rootpointstoasubtreeofanAVL_treethatisdoublyunbalanced
- //ontheright
- //Post:TheAVLpropertieshavebeenrestoredtothesubtree
- Binary_node<Record>*&right_tree=sub_root->right;
- switch(right_tree->get_balance()){
- right_tree->set_balance(equal_height);
- rotate_left(sub_root);
- cout<<"WARNING:programerrordetectedinright_balance"<<endl;
- Binary_node<Record>*sub_tree=right_tree->left;
- switch(sub_tree->get_balance()){
- right_tree->set_balance(right_higher);
- right_tree->set_balance(equal_height);
- sub_tree->set_balance(equal_height);
- rotate_right(right_tree);
- rotate_left(sub_root);
- voidAVL_tree<Record>::left_balance(Binary_node<Record>*&sub_root)
- Binary_node<Record>*&left_tree=sub_root->left;
- switch(left_tree->get_balance()){
- left_tree->set_balance(equal_height);
- rotate_right(sub_root);
- cout<<"WARNING:programerrordetectedinleft_balance"<<endl;
- Binary_node<Record>*sub_tree=left_tree->right;
- left_tree->set_balance(left_higher);
- sub_tree->set_balance(equal_height);
- rotate_left(left_tree);
- voidAVL_tree<Record>::rotate_left(Binary_node<Record>*&sub_root)
- //Pre:sub_rootpointstoasubtreeoftheAVL_tree.Thissubtreehas
- //anonemptyrightsubtree.
- //Post:sub_rootisresettopointtoitsformerrightchild,andthe
- //formersub_rootnodeistheleftchildofthenewsub_rootnode
- if(sub_root==NULL||sub_root->right==NULL)
- cout<<"WARNING:programerrordetectedinrotate_left"<<endl;
- else{
- Binary_node<Record>*right_tree=sub_root->right;
- sub_root->right=right_tree->left;
- right_tree->left=sub_root;
- sub_root=right_tree;
- voidAVL_tree<Record>::rotate_right(Binary_node<Record>*&sub_root)
- if(sub_root==NULL||sub_root->left==NULL)
- cout<<"WARNING:programerrorindetectedinrotate_right"<<endl;
- else{
- Binary_node<Record>*left_tree=sub_root->left;
- sub_root->left=left_tree->right;
- left_tree->right=sub_root;
- sub_root=left_tree;
- Error_codeAVL_tree<Record>::remove(constRecord&old_data)
- boolshorter;
- returnavl_remove(root,old_data,shorter);
- Error_codeAVL_tree<Record>::avl_remove(Binary_node<Record>*&sub_root,
- bool&shorter)
- Binary_node<Record>*temp;
- if(sub_root==NULL)returnfail;
- if(target<sub_root->data)
- returnavl_remove_left(sub_root,target,153); background-color:inherit; font-weight:bold">if(target>sub_root->data)
- returnavl_remove_right(sub_root,153); background-color:inherit; font-weight:bold">if(sub_root->left==NULL){
- temp=sub_root;
- sub_root=sub_root->right;
- deletetemp;
- shorter=if(sub_root->right==NULL){
- //Moveleftsubtreeuptodeletenode
- sub_root=sub_root->left;
- if(sub_root->get_balance()==left_higher){
- //Neithersubtreeisempty;deletefromthetaller
- temp=sub_root->left;
- while(temp->right!=NULL)temp=temp->right;
- sub_root->data=temp->data;
- avl_remove_left(sub_root,temp->data,shorter);
- temp=sub_root->right;
- while(temp->left!=NULL)temp=temp->left;
- avl_remove_right(sub_root,153); background-color:inherit; font-weight:bold">returnsuccess;
- Error_codeAVL_tree<Record>::avl_remove_right(Binary_node<Record>
- *&sub_root,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> Error_coderesult=avl_remove(sub_root->right,shorter);
- if(shorter==true)switch(sub_root->get_balance()){
- sub_root->set_balance(equal_height);
- Binary_node<Record>*temp=sub_root->left;
- switch(temp->get_balance()){
- temp->set_balance(right_higher);
- shorter= temp->set_balance(equal_height);
- Binary_node<Record>*temp_right=temp->right;
- switch(temp_right->get_balance()){
- temp->set_balance(left_higher);
- temp_right->set_balance(equal_height);
- rotate_left(sub_root->left);
- Error_codeAVL_tree<Record>::avl_remove_left(Binary_node<Record>
- *&sub_root,87); background-color:inherit; font-weight:bold">bool&shorter)
- Error_coderesult=avl_remove(sub_root->left,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> sub_root->set_balance(right_higher);
- Binary_node<Record>*temp=sub_root->right;
- Binary_node<Record>*temp_left=temp->left;
- switch(temp_left->get_balance()){
- sub_root->set_balance(left_higher);
- temp_left->set_balance(equal_height);
- rotate_right(sub_root->right);
- }
实验结果
实现如下功能:1)由{4,9,1,8,6,3,5,2,7}建AVL树B,并以括号表示法输出;2)删除B中关键字为8和2的结点,输出结果。
分析总结
采用链式存储结构实现二叉树
- 二叉树在插入的时候通过判断待插入数据与根节点数据的大小,若小于根数据,插入左树,反之插入右树(我们规定不许有重复元素);二叉树的存储结构常用于搜索等。但搜索的效率常依赖于树的结构。而树的结构与元素插入顺序有很大关系,用上述方法插入时若插入的元素是有序的,则得到的树与队列几乎没有区别,也起不到优化搜索的目的。
- 二叉树的遍历算法最为重要的一点是递归,递归使我们不必关心于具体的遍历每个节点的顺序,而只是将其看做三个部分,即左子树,根节点,右子树,具体子树的遍历仍是调用其递归函数。
3.在打印树的时候,我写的并不完美,因为对于叶子节点,理想应该不再打印括号,但我通过判断根节点不为空而调用递归函数,即只要节点有元素就会输出(,),还是表达出了树的意思,但并没有想到怎样达到简洁的效果。
实现AVL树的相关运算
- AVL树每插入,删除一个节点就会判断树的高度并改变相应节点的平衡因素,每当有节点不再满足AVL树的性质,立即通过旋转得到AVL树
- 旋转的函数及其巧妙。而插入和删除元素的函数要考虑的情况非常多,一种情况没有考虑到就可能达不到我们想要的效果,函数的编写需要极大的耐心和对编程语句的熟练掌握。很多地方我是参考书上的,别人的代码我可以理解,但我自己写可能还是会漏掉很多情况。
转载请注明出处:http://www.52php.cn/article/p-mgfuopiu-vo.html (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|