专题2:二叉树的先序、中序、后序、层次、宽度、深度遍历

二叉树的先序、中序、后序、层次、宽度、深度遍历

1.二叉树的先序、中序、后序递归遍历

(1)先序递归遍历(LeetCode144)

public static void preOrderRecur(Node head) {
		if (head == null) {
			return;
		}
		System.out.print(head.value + " ");
		preOrderRecur(head.left);
		preOrderRecur(head.right);
	}

(2)中序递归遍历(LeetCode94)

public static void inOrderRecur(Node head) {
		if (head == null) {
			return;
		}
		inOrderRecur(head.left);
		System.out.print(head.value + " ");
		inOrderRecur(head.right);
	}

(3)后序递归遍历(LeetCode145)

public static void posOrderRecur(Node head) {
		if (head == null) {
			return;
		}
		posOrderRecur(head.left);
		posOrderRecur(head.right);
		System.out.print(head.value + " ");
	}

2.二叉树的先序、中序、后序非递归遍历

(1)二叉树的先序非递归遍历

代码思想:利用栈结构,先打印中间节点,再将右孩子、左孩子压入栈,栈逆序打印,实现中左右的打印顺序。

代码实现:

public static void preOrderUnRecur(Node head) {
		System.out.print("pre-order: ");
		if (head != null) {
			Stack stack = new Stack();
			stack.add(head);
			while (!stack.isEmpty()) {
				head = stack.pop();
				System.out.print(head.value + " ");
				if (head.right != null) {
					stack.push(head.right);
				}
				if (head.left != null) {
					stack.push(head.left);
				}
			}
		}
		System.out.println();
	}

(2)二叉树的中序非递归遍历

代码思想:

  • 如果当前节点不为空,则将该节点压入栈,再向左孩子遍历(相当于一直把左边界节点压入栈)
  • 如果当前节点为空,则出栈,再遍历右孩子,实现左中右的打印顺序。

代码实现:

	public static void inOrderUnRecur(Node head) {
		System.out.print("in-order: ");
		if (head != null) {
			Stack stack = new Stack();
			while (!stack.isEmpty() || head != null) {
				if (head != null) {
					stack.push(head);
					head = head.left;
				} else {
					head = stack.pop();
					System.out.print(head.value + " ");
					head = head.right;
				}
			}
		}
		System.out.println();
	}

(3)二叉树的后序非递归遍历

代码思想:后序遍历的顺序是左右中。在栈中,逆序就是中右左,这与先序遍历很类似,只不过先序是中左右,所以我们只需要将先序的先压右孩子再压左孩子,改为先压左孩子再压右孩子,同时我们利用第二个栈实现逆序。

代码实现:

public static void posOrderUnRecur1(Node head) {
		System.out.print("pos-order: ");
		if (head != null) {
			Stack s1 = new Stack();
			Stack s2 = new Stack();
			s1.push(head);
			while (!s1.isEmpty()) {
				head = s1.pop();
				s2.push(head);
				if (head.left != null) {
					s1.push(head.left);
				}
				if (head.right != null) {
					s1.push(head.right);
				}
			}
			while (!s2.isEmpty()) {
				System.out.print(s2.pop().value + " ");
			}
		}
		System.out.println();
	}

3.二叉树的层次遍历(LeetCode102)

对付层次遍历最好的工具就是对列,每一层的节点顺序进入对列并从前到后打印。只不过需要区别哪些节点对待哪一层。

代码实现:

 public List> levelOrder(TreeNode root) {
        List> levels = new ArrayList<>();
        if (root == null) {
            return levels;
        }
        Queue queue = new LinkedList<>();
        queue.add(root);
        int level = 0;
        while (!queue.isEmpty()) {
            levels.add(new ArrayList<>());
            int level_length = queue.size();
            for (int i = 0; i < level_length; i++) {
                TreeNode node = queue.remove();
                levels.get(level).add(node.val);
                if (node.left != null) queue.add(node.left);
                if (node.right != null) queue.add(node.right);
            }
            level++;
        }
        return levels;
    }

拓展题目:之字形打印二叉树

代码思想:除了对列之外,需要使用栈空间,因为对列无法实现全部之字形的打印,而实际上,二叉树行数从0开始计数,奇数行使用栈打印,偶数行使用对列打印。

代码实现:

 public ArrayList > Print(TreeNode pRoot) {
           ArrayList> res = new ArrayList<>();
        if (pRoot == null) {
            return res;
        }
        Queue queue = new LinkedList<>();
        Stack stack = new Stack<>();
        queue.add(pRoot);
        int level = 0;
        res.add(new ArrayList());
        res.get(level++).add(pRoot.val);
        while (!queue.isEmpty()) {
            res.add(new ArrayList<>());
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode head = queue.remove();
                if (head.left != null) {
                    queue.offer(head.left);
                    if (level % 2 == 0) {   //偶数选择队列
                        res.get(level).add(head.left.val);
                    }
                    if (level % 2 != 0) {
                        stack.push(head.left);
                    }
                }
                if (head.right != null) {
                    queue.offer(head.right);
                    if (level % 2 == 0) {   //偶数选择队列
                        res.get(level).add(head.right.val);
                    }
                    if (level % 2 != 0) {
                        stack.push(head.right);
                    }
                }
            }
            /*奇数选择栈*/
            while (level % 2 != 0 && !stack.isEmpty()) {
                res.get(level).add(stack.pop().val);
            }
            level++;
        }
        res.remove(res.size()-1);
        return res;
    }

4.二叉树的宽度遍历(LeetCode662)

这里的宽度遍历是指找出二叉树中宽度最大的一层节点数。每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

代码思想:我们只需要在层次遍历的基础上加以改进,即记录当前节点的位置信息,设当前节点位置序号为pos,则它的左孩子序号为pos*2,右孩子序号为pos*2+1,用一个list辅助空间记录位置信息,在每一层遍历后比较位置区间,取最大值。

代码实现:

 public int widthOfBinaryTree(TreeNode root) {
        LinkedList list = new LinkedList<>();
        if (root == null) {
            return 0;
        }
        Queue queue = new LinkedList<>();
        queue.offer(root);
        list.add(1);  //把根节点的序号存储
        int res = 1;
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i = 2) {
                res = Math.max(res, list.getLast() - list.getFirst() + 1);
            }
        }
        return res;
    }

5.二叉树的深度遍历(LeetCode104)

二叉树的最大深度,即取从根节点到最远叶子节点的最长路径上的节点数,利用递归解决即可。

代码实现:

public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        return Math.max(left, right) + 1;
    }

 

你可能感兴趣的:(算法)