二叉树的遍历方式
- 二叉树有两种存储结构,一种是用数组存储,通过各结点下标的关系组成树,另外一种是链式存储,各节点之间以指针相连。
- 对于数组存储的二叉树,设起始下标为1,对于下标为n的某个结点,其左孩子下标为2n,右孩子为2n+1,父节点为n/2。
- 链式结构,下面的遍历都以链式结构实现
递归序
- 树的递归中,对于一个结点会访问到三次,第一次从父结点访问到,第二次从左结点访问到,第三次从右结点访问到。
遍历二叉树
- 先序:头左右
- 中序:左头右
- 后序:左右头
递归
void process(Node head){
if(head == null) return;
// 在这里输出head就是先序
process(head.left);
// 在这里输出head就是中序
process(head.right);
// 在这里输出head就是后序
}
非递归
- 使用栈进行遍历
-
-
先序遍历
-
先将头节点加入栈中,以栈不为空进行循环,每次循环弹出栈顶元素并打印,同时判断有无左右孩子,有则加入,先右后左。
-
-
-
-
后序
- 使用两个栈a,b,先将头节点加入a中,以a不为空为条件进行循环,每次循环弹出栈顶元素并加入b,同时判断有无左右孩子,有则加入a,先左后右。结束循环后依次弹出b中元素并打印。
-
中序
- 先将左边界的结点全部入栈,弹出的过程中看看是否存在右子树,存在则对右子树的左边界全部入栈,重复上述操作。
宽度优先遍历
- 通过队列实现,先放左再放右,弹出就打印
Morris遍历
-
-
细节
-
实现代码
static void morris(Node head) { // cur记录所在结点,cur不为null则一直循环 Node cur = head; while (cur != null) { System.out.print(cur.value); if (cur.left == null) { // 1.如果没有左子树就cur就向右走 cur = cur.right; } else { // 2.如果有左子树就找到左子树的最右结点mostRight Node mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { mostRight = mostRight.right; } if(mostRight.right == null){ // 2.1 如果mostRight的右指针为空则令右指针指向cur,cur向左走 mostRight.right = cur; cur = cur.left; }else { // 2.2 如果mostRight的右指针不为空则让cur向右走,并令mostRight的右指针为空 mostRight.right = null; cur = cur.right; } } } } -
morris序到先中后序
- 先序:在第一次遍历到这个结点时就进行打印,第二次则不进行打印。
- 如何判断是第一次还是第二次?如果是通过叶节点的指针遍历到的就是第二次!
- 中序:对于可以遍历到第二次的就在第二次遍历时打印,不能的就在第一次遍历时打印
-
static void morris(Node head) { // cur记录所在结点,cur不为null则一直循环 Node cur = head; while (cur != null) { if (cur.left == null) { // System.out.print(cur.value); 中序和先序都要 // 1.如果没有左子树就cur就向右走 cur = cur.right; } else { // 2.如果有左子树就找到左子树的最右结点mostRight Node mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { mostRight = mostRight.right; } if(mostRight.right == null){ // 2.1 如果mostRight的右指针为空则令右指针指向cur,cur向左走 mostRight.right = cur; // System.out.print(cur.value); 先序 cur = cur.left; }else { // 2.2 如果mostRight的右指针不为空则让cur向右走,并令mostRight的右指针为空 mostRight.right = null; // System.out.print(cur.value); 中序 cur = cur.right; } } } } - 后序,第二次来到同一个结点时逆序打印左树的右边界,最后打印整颗树的右边界
-
static void morris(Node head) { // cur记录所在结点,cur不为null则一直循环 Node cur = head; while (cur != null) { if (cur.left == null) { // 1.如果没有左子树就cur就向右走 cur = cur.right; } else { // 2.如果有左子树就找到左子树的最右结点mostRight Node mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { mostRight = mostRight.right; } if(mostRight.right == null){ // 2.1 如果mostRight的右指针为空则令右指针指向cur,cur向左走 mostRight.right = cur; cur = cur.left; }else { // 2.2 如果mostRight的右指针不为空则让cur向右走,并令mostRight的右指针为空 mostRight.right = null; printEdge(cur.left); cur = cur.right; } } } printEdge(head); } static void printEdge(Node head){ Node node = invertLink(head); head = node; while (node != null){ System.out.print(node.value); node = node.right; } invertLink(head); } static Node invert(Node head) { Node help = null; Node temp = null; while (head != null) { temp = head.right; head.right = help; help = head; head = temp; } return help; }
-
如何选择Morris和递归
- 当需要第三次汇总信息强整合用递归最优解,其他则是morris最优