数据结构-二叉树[非递归遍历](先序遍历,中序遍历,后续遍历,层次遍历)

淩亂°似流年 2022-05-20 06:54 326阅读 0赞

数据结构-二叉树[非递归遍历]

1.二叉树概念

2.二叉树的构造及删除

不得不说下二叉树的构造,本来我是想找非递归实现的,结果只看到了完全二叉树和满二叉树(下文有)的构造方法,所以这一部分我暂时函数用前序的方法构造和删除,然后二叉树的概念在上篇文:二叉树的递归实现有介绍所以放个传送门大家自己去看看。

传送门: 点击打开链接

3.完全二叉树和满二叉树的非递归构造

(1)思路:利用完全和满二叉树双亲节点与左右孩子节点编号的关系

①双亲节点编号=左孩子或右孩子节点编号/2

②孩子节点编号%2=1,该孩子节点是右孩子节点

③孩子节点编号%2=0,该孩子节点是左孩子节点

利用节点地址数组储存节点,并按照上述关系建立节点联系

(2)实现代码

  1. template<class T>
  2. int Binary_tree<T>::Create_BTree(Bt_Node<T>* &Tree) //满二叉树和完全二叉树的创建方法(非递归)
  3. {
  4. Bt_Node<T>* Bt_Node_List[MAXSIZE]; //创建节点指针数组
  5. //Bt_Node_List = new Bt_Node<T>*[MAXSIZE];
  6. Bt_Node<T>* Start_Node;
  7. T Data;
  8. int Index, FindParent; //Index为树节点编号,FindParent是查找双亲节点的编号
  9. cin >> Index >> Data; //输入树节点的编号及数据
  10. while (Index != 0 && Data != -1) //编号不为0且输入数据为-1代表结束
  11. {
  12. Start_Node = new Bt_Node<T>;
  13. Start_Node->Data = Data;
  14. Start_Node->Left_Child = NULL; //左孩子和右孩子节点先赋值空
  15. Start_Node->Right_Child = NULL;
  16. Bt_Node_List[Index] = Start_Node;
  17. if (Index != 1) //当该节点不是根节点
  18. {
  19. FindParent = Index / 2; //寻找该节点的双亲节点
  20. if (Index % 2) //若该节点编号对2取余结果为1,说明该节点是其双亲节点的右孩子节点,否则为左孩子节点
  21. Bt_Node_List[FindParent]->Right_Child = Start_Node; //双亲节点找到自己的孩子
  22. else
  23. Bt_Node_List[FindParent]->Left_Child = Start_Node;
  24. }
  25. cin >> Index >> Data; //继续输入数据
  26. }
  27. Tree = Bt_Node_List[1]; //在节点地址数组的位置为1的节点为根节点
  28. return 1;

4.二叉树的非递归遍历(正文开始)

非递归遍历的思路:递归在一定意义上就是将每一步操作入栈然后在满足停止入栈时开始出栈,所以对于二叉树的非递归遍历,也是借用栈,通过对节点的出栈入栈,实现各种遍历。

(1)先序遍历

①思路:

先序是:根左右,每一次都将一个节点出栈并访问节点数组,然后先入栈右非空节点,再入栈左非空节点(因为栈满足后进先出),直到栈空时结束遍历。

②代码实现:

  1. template<class T>
  2. void Binary_tree<T>::PreOrder_Op(Bt_Node<T>* &Tree)
  3. {
  4. Bt_Node<T>* BtNode_Stack[MAXSIZE],*Stack_Top; //BtNode_Stack[MAXSIZE]为节点指针数组,用于模拟栈操作,Stack_Top用于存放栈顶根节点
  5. int Top = -1; //初始栈空
  6. if (Tree) //根节点非空时
  7. {
  8. BtNode_Stack[++Top] = Tree; //根节点入栈
  9. while (Top > -1) //栈非空时,遍历
  10. {
  11. Stack_Top = BtNode_Stack[Top--]; //将栈顶节点出栈
  12. cout << Stack_Top->Data << endl;
  13. if (Stack_Top->Right_Child != NULL) //右孩子非空先入栈
  14. BtNode_Stack[++Top] = Stack_Top->Right_Child;
  15. if (Stack_Top->Left_Child != NULL) //左孩子非空再入栈
  16. BtNode_Stack[++Top] = Stack_Top->Left_Child;
  17. }
  18. }
  19. }

(2)中序遍历

①思路:

中序:左根右,中序都是从树的最左边节点开始遍历的,所以对于每个节点下属的子树,都是找到该子树的最左节点,将该方向上的节点全部入栈,然后在栈非空的条件下出栈一个节点,访问该节点的数据,后入栈该节点的右节点,直到当前节点空并且栈空时结束遍历。

②代码实现:

  1. template<class T>
  2. void Binary_tree<T>::InOrder_Op(Bt_Node<T>* &Tree)
  3. {
  4. Bt_Node<T>* BtNode_Stack[MAXSIZE], *Stack_Top; //BtNode_Stack[MAXSIZE]为节点指针数组,用于模拟栈操作,Stack_Top用于存放栈顶根节点
  5. int Top = -1; //初始栈空
  6. if (Tree)//根节点非空下
  7. {
  8. Stack_Top = Tree; //根节点
  9. while (Top > -1 || Stack_Top) //如果栈非空或者节点非空
  10. {
  11. while (Stack_Top) //将该方向上所有的左孩子(包括当前的双亲节点入栈)
  12. {
  13. BtNode_Stack[++Top] = Stack_Top;
  14. Stack_Top = Stack_Top->Left_Child;
  15. }
  16. if (Top > -1) //栈非空时可以访问
  17. {
  18. Stack_Top = BtNode_Stack[Top--]; //出栈节点
  19. cout << Stack_Top->Data << endl; //访问该节点元素
  20. Stack_Top = Stack_Top->Right_Child; //指向右孩子节点
  21. }
  22. }
  23. }
  24. }

(3)后序遍历

①思路:

与中序遍历相同,都是将左节点沿最左方向先入栈,但因为后续遍历的根节点是最后被访问的,所以要出栈必须满足它的左右节点均被访问,所以需要记录前一次访问的节点,如果当前节点的左子树节点刚好是前一次访问的节点,那么出栈原节点。分状态执行,一种状态是原节点模式(袖珍树的遍历),一种是新节点模式,需要入栈方向左树

②代码:

  1. template<class T>
  2. void Binary_tree<T>::PostOrder_Op(Bt_Node<T>* &Tree)
  3. {
  4. Bt_Node<T>* BtNode_Stack[MAXSIZE], *Stack_Top; //BtNode_Stack[MAXSIZE]为节点指针数组,用于模拟栈操作,Stack_Top用于存放栈顶根节点
  5. Bt_Node<T> *Last_Check; //保存刚访问过的节点
  6. int Top = -1; //初始栈空
  7. int Flag;
  8. Stack_Top = Tree;
  9. if (Tree) //根节点非空时进行遍历
  10. {
  11. do
  12. {
  13. while (Stack_Top) //将该节点所有左孩子节点入栈
  14. {
  15. BtNode_Stack[++Top] = Stack_Top;
  16. Stack_Top = Stack_Top->Left_Child;
  17. }
  18. Last_Check = NULL; //Last_Check指向栈顶节点的前一个已访问过的节点
  19. Flag = 1; //Flag=1表示处理根节点,Flag=0表示处理新的节点,此时需要将该节点的所以左孩子入栈
  20. while (Top != -1 && Flag == 1) //栈非空并且程序处于处理原节点状态
  21. {
  22. Stack_Top = BtNode_Stack[Top];
  23. if (Stack_Top->Right_Child == Last_Check) //如果已经处理完右孩子节点或者右孩子节点为空,可以访问栈顶节点
  24. {
  25. cout << Stack_Top->Data << endl;
  26. Top--;
  27. Last_Check = Stack_Top; //Last_Check指向刚刚访问过的节点
  28. }
  29. else
  30. {
  31. Stack_Top = Stack_Top->Right_Child; //准备处理右孩子节点
  32. Flag = 0;
  33. }
  34. }
  35. } while (Top!=-1);
  36. }
  37. }

(4)层次遍历

①思路:

不同于前三种遍历方式,层次遍历不能依靠完全递归实现,在上一篇二叉树递归实现中我介绍了一种方法是通过穷举二叉树的层数,并对每一层进行递归遍历,这种方法的优点是可以通过每一层遍历完换行清晰看出每一层的数据情况。而下面介绍的这种方法,是通过循环队列循环入队根节点,左节点,右节点,模拟层次遍历,而说到循环队列,什么时候比较困我就发出来吧,毕竟压了特别久。

②代码:

  1. template<class T>
  2. void Binary_tree<T>::LevelOrder_Op(Bt_Node<T>* &Tree)
  3. {
  4. Bt_Node<T> *Queue_Top; //队头节点
  5. Bt_Node<T>* BtNode_Queue[MAXSIZE]; //定义循环队列
  6. int Front, Rear; //队头,队尾
  7. Front = Rear = -1;
  8. BtNode_Queue[++Rear] = Tree; //入队根节点
  9. while (Front != Rear) //队未空
  10. {
  11. Front = (Front + 1) % MAXSIZE; //循环队列特有的队头循环(通过取余实现)
  12. Queue_Top = BtNode_Queue[Front]; //将队头节点出队
  13. cout << Queue_Top->Data << endl; //访问节点数据
  14. if (Queue_Top->Left_Child != NULL) //左孩子节点非空入队
  15. {
  16. Rear = (Rear + 1) % MAXSIZE;
  17. BtNode_Queue[Rear] = Queue_Top->Left_Child;
  18. }
  19. if (Queue_Top->Right_Child != NULL) //右孩子节点非空入队
  20. {
  21. Rear = (Rear + 1) % MAXSIZE;
  22. BtNode_Queue[Rear] = Queue_Top->Right_Child;
  23. }
  24. }
  25. }

5.类实现代码(全部)

  1. #include<iostream>
  2. using namespace std;
  3. #define MAXSIZE 100
  4. template<class T>
  5. struct Bt_Node
  6. {
  7. T Data;
  8. Bt_Node* Left_Child;
  9. Bt_Node* Right_Child;
  10. };
  11. template<class T>
  12. class Binary_tree
  13. {
  14. private:
  15. Bt_Node<T> *Tree;
  16. /*内部实现函数*/
  17. int Create_BTree(Bt_Node<T>* &Tree); //创建二叉树(构造内调用)
  18. int Distory_BTree(Bt_Node<T>* &Tree); //析构二叉树的递归子函数(在析构内调用)
  19. void PreOrder_Op(Bt_Node<T>* &Tree); //先序遍历(非递归实现函数)
  20. void InOrder_Op(Bt_Node<T>* &Tree); //中序遍历(非递归实现函数)
  21. void PostOrder_Op(Bt_Node<T>* &Tree); //后序遍历(非递归实现函数)
  22. void LevelOrder_Op(Bt_Node<T>* &Tree); //层次遍历的非递归子函数
  23. public:
  24. /*接口函数*/
  25. Binary_tree(); //类构造
  26. ~Binary_tree(); //析构
  27. void PreOrder(); //先序遍历
  28. void InOrder(); //中序遍历
  29. void PostOrder();
  30. void LevelOrder(); //层次遍历
  31. };
  32. /*template<class T>
  33. int Binary_tree<T>::Create_BTree(Bt_Node<T>* &Tree) //满二叉树和完全二叉树的创建方法(非递归)
  34. {
  35. Bt_Node<T>* Bt_Node_List[MAXSIZE]; //创建节点指针数组
  36. //Bt_Node_List = new Bt_Node<T>*[MAXSIZE];
  37. Bt_Node<T>* Start_Node;
  38. T Data;
  39. int Index, FindParent; //Index为树节点编号,FindParent是查找双亲节点的编号
  40. cin >> Index >> Data; //输入树节点的编号及数据
  41. while (Index != 0 && Data != -1) //编号不为0且输入数据为-1代表结束
  42. {
  43. Start_Node = new Bt_Node<T>;
  44. Start_Node->Data = Data;
  45. Start_Node->Left_Child = NULL; //左孩子和右孩子节点先赋值空
  46. Start_Node->Right_Child = NULL;
  47. Bt_Node_List[Index] = Start_Node;
  48. if (Index != 1) //当该节点不是根节点
  49. {
  50. FindParent = Index / 2; //寻找该节点的双亲节点
  51. if (Index % 2) //若该节点编号对2取余结果为1,说明该节点是其双亲节点的右孩子节点,否则为左孩子节点
  52. Bt_Node_List[FindParent]->Right_Child = Start_Node; //双亲节点找到自己的孩子
  53. else
  54. Bt_Node_List[FindParent]->Left_Child = Start_Node;
  55. }
  56. cin >> Index >> Data; //继续输入数据
  57. }
  58. Tree = Bt_Node_List[1]; //在节点地址数组的位置为1的节点为根节点
  59. return 1;
  60. }*/
  61. template<class T>
  62. int Binary_tree<T>::Create_BTree(Bt_Node<T>* &Tree)
  63. {
  64. T Data;
  65. cin >> Data;
  66. if (Data == -1)
  67. Tree = NULL;
  68. else
  69. {
  70. Tree = new Bt_Node<T>;
  71. Tree->Data = Data;
  72. Create_BTree(Tree->Left_Child);
  73. Create_BTree(Tree->Right_Child);
  74. }
  75. return 1;
  76. }
  77. template<class T>
  78. int Binary_tree<T>::Distory_BTree(Bt_Node<T>* &Tree)
  79. {
  80. if (Tree)
  81. {
  82. Bt_Node<T>* LeftTree = Tree->Left_Child;
  83. Bt_Node<T>* RightTree = Tree->Right_Child;
  84. //cout << Tree->Data << endl;
  85. delete Tree;
  86. if (LeftTree)
  87. Distory_BTree(LeftTree);
  88. if (RightTree)
  89. Distory_BTree(RightTree);
  90. }
  91. return 0;
  92. }
  93. template<class T>
  94. void Binary_tree<T>::PreOrder_Op(Bt_Node<T>* &Tree)
  95. {
  96. Bt_Node<T>* BtNode_Stack[MAXSIZE],*Stack_Top; //BtNode_Stack[MAXSIZE]为节点指针数组,用于模拟栈操作,Stack_Top用于存放栈顶根节点
  97. int Top = -1; //初始栈空
  98. if (Tree) //根节点非空时
  99. {
  100. BtNode_Stack[++Top] = Tree; //根节点入栈
  101. while (Top > -1) //栈非空时,遍历
  102. {
  103. Stack_Top = BtNode_Stack[Top--]; //将栈顶节点出栈
  104. cout << Stack_Top->Data << endl;
  105. if (Stack_Top->Right_Child != NULL) //右孩子非空先入栈
  106. BtNode_Stack[++Top] = Stack_Top->Right_Child;
  107. if (Stack_Top->Left_Child != NULL) //左孩子非空再入栈
  108. BtNode_Stack[++Top] = Stack_Top->Left_Child;
  109. }
  110. }
  111. }
  112. template<class T>
  113. void Binary_tree<T>::InOrder_Op(Bt_Node<T>* &Tree)
  114. {
  115. Bt_Node<T>* BtNode_Stack[MAXSIZE], *Stack_Top; //BtNode_Stack[MAXSIZE]为节点指针数组,用于模拟栈操作,Stack_Top用于存放栈顶根节点
  116. int Top = -1; //初始栈空
  117. if (Tree)//根节点非空下
  118. {
  119. Stack_Top = Tree; //根节点
  120. while (Top > -1 || Stack_Top) //如果栈非空或者节点非空
  121. {
  122. while (Stack_Top) //将该方向上所有的左孩子(包括当前的双亲节点入栈)
  123. {
  124. BtNode_Stack[++Top] = Stack_Top;
  125. Stack_Top = Stack_Top->Left_Child;
  126. }
  127. if (Top > -1) //栈非空时可以访问
  128. {
  129. Stack_Top = BtNode_Stack[Top--]; //出栈节点
  130. cout << Stack_Top->Data << endl; //访问该节点元素
  131. Stack_Top = Stack_Top->Right_Child; //该节点的右孩子节点入栈
  132. }
  133. }
  134. }
  135. }
  136. template<class T>
  137. void Binary_tree<T>::PostOrder_Op(Bt_Node<T>* &Tree)
  138. {
  139. Bt_Node<T>* BtNode_Stack[MAXSIZE], *Stack_Top; //BtNode_Stack[MAXSIZE]为节点指针数组,用于模拟栈操作,Stack_Top用于存放栈顶根节点
  140. Bt_Node<T> *Last_Check; //保存刚访问过的节点
  141. int Top = -1; //初始栈空
  142. int Flag;
  143. Stack_Top = Tree;
  144. if (Tree) //根节点非空时进行遍历
  145. {
  146. do
  147. {
  148. while (Stack_Top) //将该节点所有左孩子节点入栈
  149. {
  150. BtNode_Stack[++Top] = Stack_Top;
  151. Stack_Top = Stack_Top->Left_Child;
  152. }
  153. Last_Check = NULL; //Last_Check指向栈顶节点的前一个已访问过的节点
  154. Flag = 1; //Flag=1表示处理根节点,Flag=0表示处理新的节点,此时需要将该节点的所以左孩子入栈
  155. while (Top != -1 && Flag == 1) //栈非空并且程序处于处理原节点状态
  156. {
  157. Stack_Top = BtNode_Stack[Top];
  158. if (Stack_Top->Right_Child == Last_Check) //如果已经处理完右孩子节点或者右孩子节点为空,可以访问栈顶节点
  159. {
  160. cout << Stack_Top->Data << endl;
  161. Top--;
  162. Last_Check = Stack_Top; //Last_Check指向刚刚访问过的节点
  163. }
  164. else
  165. {
  166. Stack_Top = Stack_Top->Right_Child; //准备处理右孩子节点
  167. Flag = 0;
  168. }
  169. }
  170. } while (Top!=-1);
  171. }
  172. }
  173. template<class T>
  174. void Binary_tree<T>::LevelOrder_Op(Bt_Node<T>* &Tree)
  175. {
  176. Bt_Node<T> *Queue_Top; //队头节点
  177. Bt_Node<T>* BtNode_Queue[MAXSIZE]; //定义循环队列
  178. int Front, Rear; //队头,队尾
  179. Front = Rear = -1;
  180. BtNode_Queue[++Rear] = Tree; //入队根节点
  181. while (Front != Rear) //队未空
  182. {
  183. Front = (Front + 1) % MAXSIZE; //循环队列特有的队头循环(通过取余实现)
  184. Queue_Top = BtNode_Queue[Front]; //将队头节点出队
  185. cout << Queue_Top->Data << endl; //访问节点数据
  186. if (Queue_Top->Left_Child != NULL) //左孩子节点非空入队
  187. {
  188. Rear = (Rear + 1) % MAXSIZE;
  189. BtNode_Queue[Rear] = Queue_Top->Left_Child;
  190. }
  191. if (Queue_Top->Right_Child != NULL) //右孩子节点非空入队
  192. {
  193. Rear = (Rear + 1) % MAXSIZE;
  194. BtNode_Queue[Rear] = Queue_Top->Right_Child;
  195. }
  196. }
  197. }
  198. template<class T>
  199. Binary_tree<T>::Binary_tree()
  200. {
  201. Create_BTree(Tree);
  202. }
  203. template<class T>
  204. Binary_tree<T>::~Binary_tree()
  205. {
  206. Distory_BTree(Tree);
  207. }
  208. template<class T>
  209. void Binary_tree<T>::PreOrder()
  210. {
  211. PreOrder_Op(Tree);
  212. }
  213. template<class T>
  214. void Binary_tree<T>::InOrder()
  215. {
  216. InOrder_Op(Tree);
  217. }
  218. template<class T>
  219. void Binary_tree<T>::PostOrder()
  220. {
  221. PostOrder_Op(Tree);
  222. }
  223. template<class T>
  224. void Binary_tree<T>::LevelOrder()
  225. {
  226. LevelOrder_Op(Tree);
  227. }

日常BB:

这算是这半年来觉得最难啃的代码了,可能自己比较弱鸡,不过的确写完之后我自己都还是对整个过程有点模糊,慢慢看吧~共勉,其实后面还有难的,就是将二叉树线索化,重点来了,下篇见~

发表评论

表情:
评论列表 (有 0 条评论,326人围观)

还没有评论,来说两句吧...

相关阅读