数据结构面试之六——二叉树的常见操作2(非递归遍历&二叉排序树)

简介: 《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。

题注

《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。

接上一节第五部分,主要分析二叉树的非递归遍历和二叉排序树的操作。

1. 非递归中序遍历

//1.依次将根节点root的左子树入栈,直到lchild=NULL,执行2

//2.将栈的元素出栈、访问;将当前指针指向节点的rchild,循环遍历。直到栈空为止!

      

template<typenameelemType>
       voidbinaryTreeType<elemType>::noRecursionInorderTraversal()                      //非递归中序遍历
       {
              cout<< "noRecursionInorderTraversal--------------------------->"<< endl;
              linkedStackType<nodeType<elemType>* > stack;
              nodeType<elemType>*current = root;
              while(current!= NULL || !stack.isEmptyStack())  //或者||
              {
                     if(current!= NULL)
                     {
                            stack.push(current);
                            current= current->llink;
                     }
                     else
                     {
                            stack.pop(current);
                            cout<< current->info << "\t"; //出栈的时候访问节点
                            current= current->rlink;
                     }
              }
              cout<< endl;
              cout<< "<------------------------noRecursionInorderTraversal"<< endl;
       }

2. 非递归先序遍历

//在中序遍历的基础上,访问次序发生变化;

//先序遍历,需要先逐个遍历根节点,然后依次处理其左、右孩子节点。

     

  template<typenameelemType>
       voidbinaryTreeType<elemType>::noRecursionPreorderTraversal()                     //非递归前序遍历
       {
              cout<<"noRecursionPreorderTraversal--------------------------->"<< endl;
              linkedStackType<nodeType<elemType>* > stack;
              nodeType<elemType>*current = root;
              while(current!= NULL || !stack.isEmptyStack())  //或者||
              {
                     if(current!= NULL)
                     {
                            cout<< current->info << "\t";   //先访问节点后入栈
                            stack.push(current);
                            current= current->llink;
                     }
                     else
                     {
                            stack.pop(current);
                            current= current->rlink;
                     }
              }
              cout<< endl;
              cout<< "<------------------------noRecursionPreorderTraversal"<< endl;
       }

3.      非递归后序遍历

由于访问的顺序为先左子树、然后右子树,最后根节点。并且对于每一个节点都是上述操作,所以,对于遍历来讲,需要识别当前节点类型是根(相对)、左孩子节点 、右孩子节点。故,我们设定了flag标记变量,flag=0初始标记,节点尚未入栈;在访问左孩子之前将flag置为1;在访问右孩子之前将flag置为2;并且在访问右孩子之后,将flag置为0。

//后序非递归遍历比较复杂..

     

  template<typenameelemType>
voidbinaryTreeType<elemType>::noRecursionPostorderTraversal()                    //非递归后序遍历
       {
              cout<<"noRecursionPostorderTraversal--------------------------->"<< endl;
              linkedStackType<nodeType<elemType>* > stack;
              linkedStackType<int>intStack;                       //标记位同步栈.
              nodeType<elemType>*current = root;
              intnflag = 0;                                      //初始标记为0.
              if(current== NULL)
              {
                     cout<< "The Stack is Empty!" << endl;
              }
              else
              {
                     //1.将头节点先入栈,
                     stack.push(current);
                     intStack.push(1);
               current = current->llink;        //注意此处需要调整指向******
                     while(!stack.isEmptyStack()&& !intStack.isEmptyStack())         
                     {
                            if(current!= NULL && nflag == 0)                                     
                            {
                               stack.push(current);
                                   intStack.push(1);   //标记位为1,[在访问左孩子之前,将其值置为1]。
                               current = current->llink;
                            }
                            else
                            {
                                   stack.pop(current);
                                   intStack.pop(nflag);    //此时的标记位为返回值,需要根据其做判断
                                   if(nflag== 1)         //说明下一步需要入栈的为右孩子.
                                   {
                                          stack.push(current);   //继续将该节点入栈,                                                              
                                         intStack.push(2);      //但[在访问右孩子之前,将其置为2]。
                                          current= current->rlink;           //访问右节点,
                                          nflag= 0;                                  //置标记位为0
                                   }
                                   else
                                   {
                                          cout<< current->info << " ";  //待左右子树都为空再访问节点。
                                   }
                            }
                     }
                     cout<< endl;
                     cout<< "<------------------------noRecursionPostorderTraversal"<< endl;
              }    
       }

 

4. 二叉排序树的搜索操作

明确概念,国内、国外的著作里提及的下三个概念等价,二叉搜索树=二叉查找树=二叉排序树。

//二叉排序树的查找存在以下几种情况:

//1.链表为空,提示并返回;

//2.链表非空,需要循环查找直到指针为空,若存在,则bfound=true;否则查找至最后bfound=缺省false。

template <class elemType>
boolbSearchTreeType<elemType>::search(const elemType& searchItem)
{
       nodeType<elemType>*current = new nodeType<elemType>;
       boolbFound = false;
 
       if(root== NULL)
       {
              cout<< "The bSearchTree is NULL\n";       //case1: 链表为空!
              returnfalse;
       }
       else
       {
              current= root;
              while(current!= NULL && !bFound) //case2:在链表中查找,根据大小锁定左、右子树.
              {
                     if(current->info== searchItem)
                     {
                            bFound= true;
                     }
                     elseif(current->info > searchItem)
                     {
                            current= current->llink;              //左子树
                     }
                     elseif(current->info < searchItem)
                     {
                            current= current->rlink;             //右子树
                     }
              }
       }
 
       returnbFound;
}
5.      二叉排序树的插入存在以下几种情况:

//1.链表为空,插入元素即为根节点;

//2.链表非空,需要寻找插入位置后插入。

//2.1插入元素已经存在,则提示出错。

//2.2总能找到大于或小于某节点的位置,记录trailcurrent完成插入操作。


template <class elemType>
voidbSearchTreeType<elemType>::insert(const elemType& insertItem)
{
       nodeType<elemType>*newNode = new nodeType<elemType>;
       nodeType<elemType>*current;
       nodeType<elemType>*trailCurrent;
 
       newNode->info= insertItem;
       newNode->llink= NULL;
       newNode->rlink= NULL;
 
       if(root== NULL)
       {
              root= newNode;                                //case1:树为空.
       }
       else
       {
              current= root;
              while(current!= NULL)                          //case2,3,4搜索才知道!
              {
                     trailCurrent= current;
                     if(current->info== insertItem)
                     {
                            cout<< "the elem is already exist!\n";  //case2:元素已经存在
                            return;
                     }
                     else
                     {
                            if(current->info> insertItem)
                            {
                                   current= current->llink;           //case3:锁定左侧位置...
                            }
                            else
                            {
                                   current= current->rlink;           //case4:锁定右侧位置...
                            }
                     }
              }//endwhile
 
              //case3,4根据大小进行链接
              if(trailCurrent->info< insertItem)           
              {
                     trailCurrent->rlink= newNode;
              }
              else
              {
                     trailCurrent->llink= newNode;
              }
 
       }//end else
}

6.      二叉排序树的删除存在以下几种情况【此处可能复杂些】:
//删除一个节点,要首先判断元素值在二叉排序树中是否存在,

//若不存在则返回;

//若存在则需要锁定其对应位置为1根节点;2叶节点;3其余节点。

//根据要删除的节点是否含有左右子树的不同,分为4种情况考虑,

//见deleteFromTree()函数。


template <class elemType>
voidbSearchTreeType<elemType>::deleteNode(const elemType& deleteItem)
{
       //1.查找节点
       //2.1找不到,不存在;
       //2.2找到,删除,调用函数
       nodeType<elemType>*current;
       nodeType<elemType>*trailCurrent;
       boolbFound = false;
 
       if(root== NULL)
       {
              cout<< "Can't delete an Empty BST" << endl;
              return;
       }
       else
       {
              current= root;
              trailCurrent= root;
              while(current != NULL && !bFound)
              {
                     if(current->info== deleteItem)
                     {
                            bFound= true;
                     }
                     elseif(current->info > deleteItem)
                     {
                            trailCurrent= current;
                            current= current->llink;    //左
                     }
                     else
                     {
                            trailCurrent= current;
                            current= current->rlink;   //右
                     }
              }//endwhile
 
              if(current== NULL)
              {
                     cout<< deleteItem << " is not Exist in the BST!\n" <<endl;
              }
              elseif(bFound)
              {
                     if(current== root)
                     {
                            deleteFromTree(root);                  //可能是根节点
                     }
                     elseif(trailCurrent->info > deleteItem)
                     {
                            deleteFromTree(trailCurrent->llink);//左半分支,调整trailCurrent的指向
                     }
                     elseif(trailCurrent->info < deleteItem)
                     {
                            deleteFromTree(trailCurrent->rlink);  //右半分支,调整trailCurrent的指向
                     }
              }//endif bFound
         }//end else
}


//[原理]:某节点的前驱是该节点左子树的最右端的节点(中序遍历的结果)


template <class elemType>
voidbSearchTreeType<elemType>::deleteFromTree(nodeType<elemType>*&p)
{
       nodeType<elemType>*temp;
       nodeType<elemType>*current;
       nodeType<elemType>*trailCurrent;
 
       if(p== NULL)
       {
              cout<< "The BST is NULL!" << endl;
              return;
       }
       if(p->llink== NULL && p->rlink == NULL)      //情况1,左右节点都为空(叶节点)
       {
              temp= p;
              p= NULL;
              deletetemp;
       }
       elseif( p->rlink == NULL)                     //情况2,右子树为空,左非空
       {
              temp= p;
              p= temp->llink;
              deletetemp;
       }
       elseif(p->llink == NULL)                      //情况3,左子树为空,右非空
       {
              temp= p;
              p= temp->rlink;
              deletetemp;
       }
       else                           //情况4,左右都非空[用中序遍历的前一个节点替换]
       {
              current= p->llink;
              trailCurrent= NULL;
 
              while(current->rlink!= NULL)
              {
                     trailCurrent= current;   //trailCurrent最终指向准备删除节点的前一个节点
                     current= current->rlink;
              }
 
              p->info= current->info;                //信息赋值
 
              if(trailCurrent== NULL)              //仅一个左孩子节点
              {
                     p->rlink = current->llink;         
              }
              else
              {
                     trailCurrent->rlink= current->llink; //给删除前点的前面一个节点调整指针指向
              }
              deletecurrent;
       }
 
}

作者:铭毅天下
来源:CSDN
原文:https://blog.csdn.net/laoyang360/article/details/7884449
版权声明:本文为博主原创文章,转载请附上博文链接!

相关文章
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
486 10
 算法系列之数据结构-二叉树
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
554 12
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
304 10
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
714 3
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
476 4
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
1964 9
|
数据采集 存储 算法
【C++数据结构——图】图的遍历(头歌教学实验平台习题) 【合集】
本文介绍了图的遍历算法,包括深度优先遍历(DFS)和广度优先遍历(BFS)。深度优先遍历通过递归方式从起始节点深入探索图,适用于寻找路径、拓扑排序等场景;广度优先遍历则按层次逐层访问节点,适合无权图最短路径和网络爬虫等应用。文中提供了C++代码示例,演示了如何实现这两种遍历方法,并附有测试用例及结果,帮助读者理解和实践图的遍历算法。
889 0
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
661 1
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
386 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)

热门文章

最新文章