二叉查找树之——非递归实现
来源:互联网 发布:linux手机版 编辑:程序博客网 时间:2024/04/29 04:36
上一一篇文章介绍了二叉查找树的递归实现,递归算法以简洁明了,便于阅读与理解,但是只要是与递归有关,就少不了递归调用栈,也就是函数栈,这会导致栈溢出。假如递归层次达到10000次,程序就崩溃了,二叉查找树当插入的节点是升序或者逆序,就形成了链表,这样就形成了10000层递归。并且,递归没有非递归效率高,语音在于函数调用会相对耗时。所以非递归算法在安全性和效率上更实用。下面对二叉查找操作的非递归算法实现。
查找节点,很简单,通过循环来查找,代码如下:
Position CBinarySearchTreeNoRecursion::find( int iElement,const Root pRoot ){ if ( NULL == pRoot ) { return NULL; } TreeNode* pNode = pRoot; while ( pNode != NULL ) { if ( iElement < pNode->idata ) { pNode = pNode->lchild; }else if ( iElement > pNode->idata ) { pNode = pNode->rchild; }else { visit(pNode); return pNode; //找到则返回 } } return NULL;}
查找最小元素,一直往左走,找到后访问,代码如下:
Position CBinarySearchTreeNoRecursion::findMin( const Root pRoot ){if ( NULL == pRoot ){return NULL;} TreeNode *pNode = pRoot; while ( pNode->lchild != NULL) //一直走到最左边{ pNode = pNode->lchild;}visit(pNode);return pNode;}查找最大元素,一直往右走,找到后访问,代码如下:
Position CBinarySearchTreeNoRecursion::findMax( const Root pRoot ){if ( NULL == pRoot ){return NULL;}TreeNode *pNode = pRoot;while ( pNode->rchild != NULL) //一直走到最右{pNode = pNode->rchild;}visit(pNode);return pNode;}插入一个节点,就要找到位置后插入,因为找到后,遍历指针为NULL,就需要一个指针尾随着遍历指针,这样就可以插入后返回正确位置的指针,代码如下:
Root CBinarySearchTreeNoRecursion::insertNode( int iElement,Root pRoot ){if ( NULL == pRoot ){return NULL;}TreeNode* pNode = NULL;TreeNode* pTemp = pRoot;while ( pRoot != NULL ) //找到插入的位置{ pNode = pRoot;//保存上一个节点的指针if ( iElement < pRoot->idata ){pRoot = pRoot->lchild;}else if (iElement > pRoot->idata ){pRoot = pRoot->rchild;}else{pRoot->uiTimes++;return pTemp;}}if ( iElement < pNode->idata) //插入该位置的左边还是右边{pNode->lchild = new TreeNode(iElement);}else if (iElement > pNode->idata ){pNode->rchild = new TreeNode(iElement);}return pNode;}二叉树的非递归删除也是很复杂,首先,让我们用一个公用的删除策略,用删除的节点的右子树的最小节点代替被删除节点,这样就可以满足二叉树的定义:左小右大。删除时,又要看看特殊情况,如果删除的节点没有右孩子怎么处理?只有一个节点或者没有节点怎么处理?下面让我们来分情况讨论:
删除的节点有左右子树:选取右子树的最小值代替删除节点,然后删除最小节点。
删除的节点只有一个或着没有孩子:需要用一个指针跟踪要删除的节点的上一个节点位置,这样就可以删除节点后设置它的父亲节点左右子树正确的指针。
更多细节请看如下代码:
Root CBinarySearchTreeNoRecursion::deleteNode( int iElement,Root pRoot ){if ( NULL == pRoot ){return pRoot;} Root pNode = pRoot; //遍历用的指针Root pLastNode = pNode; //遍历指针的上一个节点的指针Root pMinOnRight = NULL; //右子树最小节点的指针Root pSecondMinOnRight = NULL; //右子树次最小节点的指针 while ( pNode != NULL ){ if ( iElement < pNode->idata ) {pLastNode = pNode;pNode = pNode->lchild; }else if ( iElement > pNode->idata ) {pLastNode = pNode;pNode = pNode->rchild; }else //找到要删除的节点{if ( pNode->lchild != NULL && pNode->rchild != NULL )//有两个孩子情况{pSecondMinOnRight = pNode;pMinOnRight = pNode->rchild;while ( pMinOnRight->lchild != NULL) //删除策略:用右子树最小节点代替要删除的节点。找到最小的和次最小的节点{pSecondMinOnRight = pMinOnRight;pMinOnRight = pMinOnRight->lchild; } pNode->idata = pMinOnRight->idata; //将找到的最小节点放到要删除的节点位置,其实是值交换 if ( NULL == pMinOnRight->rchild ) //找到的最小节点是叶子节点{pSecondMinOnRight->lchild = NULL;}else //找到的最小节点是非叶子节点,则要处理它的右孩子,将右孩子连接到最小节点的父亲节点处{pSecondMinOnRight->lchild = pMinOnRight->rchild;}printf("delete the element: %d\n",pMinOnRight->idata);delete pMinOnRight;pMinOnRight = NULL;return pRoot;}else//一个或者零个孩子的情况{ if ( pLastNode->lchild == pNode ) //要删除的节点是左子树还是右子树 { if ( NULL == pNode->lchild ) //删除的节点是左节点 { pLastNode->lchild = pNode->rchild; //如果删除节点的左子树是空,则返回右子树 }else if ( NULL == pNode->rchild ) { pLastNode->lchild = pNode->lchild;//如果删除节点的右子树是空,则返回左子树 } } else if( pLastNode->rchild == pNode )//删除的节点是右节点 { if ( NULL == pNode->lchild ) { pLastNode->rchild = pNode->rchild; }else if ( NULL == pNode->rchild ) { pLastNode->rchild = pNode->lchild; } } printf("delete the element: %d\n",pNode->idata); delete pNode; pNode = NULL; return pRoot;}}}return pRoot;}下面来讲解遍历操作,遍历需要用到栈来保存将要被访问的节点指针,遍历有一个特点,就是中后遍历,只要遍历的左孩子,就是相当于遍历了左节点和跟节点,所以主要关注的是右子树。
前序遍历:根左右。先跟后左右子树,这个代码很好写,只需要将根节点入栈,然后出栈访问后再将其左右子树入栈,然后重复这个过程直到栈空,代码如下:
void CBinarySearchTreeNoRecursion::preOrder( const Root pRoot ){ if ( NULL == pRoot ) {return; } stack<TreeNode*> nodeStack;nodeStack.push(pRoot);TreeNode* pNode = pRoot; while ( !nodeStack.empty() ){pNode = nodeStack.top();nodeStack.pop();visit(pNode);//访问根节点的逻辑if ( pNode->rchild != NULL ){nodeStack.push(pNode->rchild);}if ( pNode->lchild != NULL ){nodeStack.push(pNode->lchild);}}}中序的遍历解放方案是:将一直往左找,找到最左边的节点后,访问该节点,然后出栈,将它的右子树入栈访问,重复这个过程。代码如下:
void CBinarySearchTreeNoRecursion::inOrder( const Root pRoot ){if ( NULL == pRoot ){return;}stack<TreeNode*> nodeStack;TreeNode* pNode = pRoot;while ( pNode || !nodeStack.empty() ){if ( pNode != NULL ){nodeStack.push(pNode); //遍历左子树pNode = pNode->lchild;}else{pNode = nodeStack.top(); nodeStack.pop();visit(pNode);//访问左节点的逻辑pNode = pNode->rchild; //遍历右子树}}}后序遍历比较复杂,因为不能单单通过入栈出栈来全部访问,必须设置一个标志,标志这个节点的右子树是否已经访问了,如果没有访问,则访问,否则将遍历右子树。
删除策略:首先将左边全部入栈,然后出栈访问,看看它的右子树是否已经访问,如果没有,则访问右子树否则访问它。必须用到一个辅助数据结构,这里用一个标志栈来与节点栈匹配,看对应的节点栈顶元素的右子树是否已经访问,false为未访问,true为已访问。
void CBinarySearchTreeNoRecursion::posOrder( const Root pRoot ){ if ( NULL == pRoot ) {return; }TreeNode* pNode = pRoot;stack<TreeNode*> nodeStack; stack<bool> flageStack; //标志栈,false:未访问,true:已访问while ( pNode != NULL ) //将树的最左边节点全部入栈{nodeStack.push(pNode);flageStack.push(false); //这两个栈有同时出入,节点栈出栈,则标志栈也要出入。pNode = pNode->lchild;}while ( !nodeStack.empty() ){ pNode = nodeStack.top();if (NULL == pNode->rchild || flageStack.top() ) //如果没有右子树或者右子树已经访问,则可以访问这个节点。{ nodeStack.pop();flageStack.pop();visit(pNode);}else //右孩子存在并且这个节点没有被访问,则遍历右子树{flageStack.pop();flageStack.push(true); //因为下面将右子树入栈,右子树后肯定会先于它被访问 //所以这里可以设置这个节点的右孩子已经被访问。pNode = pNode->rchild; while ( pNode != NULL ) //遍历右子树{nodeStack.push(pNode);flageStack.push(false);pNode = pNode->lchild; }}}}
- 二叉查找树之——非递归实现
- 非递归实现二叉查找树
- 二叉查找树非递归实现
- 树——二叉树遍历之非递归实现
- 二叉查找树之——递归实现
- 数据结构之二叉查找树的递归和非递归实现的一些方法
- 查找树——搜索二叉树(非递归)
- 二叉树遍历之非递归实现
- BinarySearchTree(2)二叉查找树链式非递归实现
- 非递归实现二叉查找树的后序遍历
- 非递归实现二叉查找树插入操作
- 【数据结构】二叉查找树【续】之非递归遍历
- 查找二叉树的实现(查找,删除,插入)分别用递归和非递归
- 二叉树(2)——遍历的非递归实现
- 二叉树的非递归遍历——java实现
- 数据结构(六)——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现
- 数据结构(六)——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现
- 数据结构(六)——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现
- 09.集合框架(二)【Set】【HashSet】【TreeSet】
- Android源码(4.3_r2)下载,Done!
- [Linux Settings]eclipse命令行启动和Dash启动
- C、C++能成为C/C++吗
- 网络拓扑结构
- 二叉查找树之——非递归实现
- OpenMP中的数据处理子句
- 第十一周项目4-计分器啊
- 视频会议十大开源项目排行
- 额,这个。。。
- ASP.NET 2.0个性化配置--Profile
- 使scp不用输入密码
- Python开发技术详解-笔记_第07章-文件的处理
- Linux系列-网络基础、网络配置