二叉树的N中遍历方式和拓展应用
(一)创建二叉树,如下图所示,一个标准的二叉树是所有位于根节点的左侧的子节点都是比根节点小的,右侧则都是大于根节点的。
public class BinaryNode { public int val; public BinaryNode left; public BinaryNode right; public BinaryNode(int val) { this.val = val; } } public class BinaryTree { public static BinaryNode createBinaryTree(int[] array) { BinaryNode root = new BinaryNode(array[0]); for (int i = 1; i < array.Length; i++) { insertBinaryTreeNode(root, array[i]); } return root; } private static void insertBinaryTreeNode(BinaryNode root, int val) { if(root.val >= val) { if (root.left == null) { root.left = new BinaryNode(val); return; } else { insertBinaryTreeNode(root.left, val); } } else { if (root.right == null) { root.right = new BinaryNode(val); return; } else insertBinaryTreeNode(root.right, val); } } }createBinaryTree
(二)前序遍历:根节点-左节点-右节点(即输出为:8,3,2,1,5,4,6,20,15,13,16,29,21,30)
public static List<int>preOrderTraverse(BinaryNode root) { List<int> nodes = new List<int>(); subPreOrderTraverse(root, nodes); return nodes; } private static void subPreOrderTraverse(BinaryNode root, List<int> nodes) { if(root != null) { nodes.Add(root.val); subPreOrderTraverse(root.left, nodes); subPreOrderTraverse(root.right, nodes); } }preOrderTraverse
不使用递归方法实现的前序遍历,即深度优先遍历(depth first search, DFS),使用栈实现。
public static List<int>depthFirstSearch(BinaryNode root) { StackdepthFirstSearchstack = new Stack (); stack.Push(root); List<int> nodes = new List<int>(); while (stack.Count != 0) { BinaryNode node = stack.Pop(); nodes.Add(node.val); if (node.right != null) stack.Push(node.right); if (node.left != null) stack.Push(node.left); } return nodes; }
(三)中序遍历:左节点-根节点-右节点(即输出为:1,2,3,4,5,6,8,13,15,16,20,21,29,30)。可见中序遍历的特点是输出的是升序数组。
public static List<int>inOrderTraverse(BinaryNode root) { List<int> nodes = new List<int>(); subInOrderTraverse(root, nodes); return nodes; } private static void subInOrderTraverse(BinaryNode root, List<int> nodes) { if (root != null) { subInOrderTraverse(root.left, nodes); nodes.Add(root.val); subInOrderTraverse(root.right, nodes); } }inOrderTraverse
中序遍历的非递归方法实现,同样使用栈。
public static List<int>inOrderTraverseNonRecursion(BinaryNode root) { List<int> nodes = new List<int>(); StackinOrderTraverseNonRecursionstack = new Stack (); BinaryNode node = root; while(node!=null || stack.Count != 0) { if (node != null) { stack.Push(node); node = node.left; } else { node = stack.Pop(); nodes.Add(node.val); node = node.right; } } return nodes; }
根据中序遍历能得到升序的数组的特点,可以用于解决验证二叉树的问题。leetcode98:给定一个二叉树,判断其是否是一个有效的二叉搜索树。解题思路便可以使用中序遍历得到数组,判断是否为升序数组。以及求节点的最小差问题等(530)。
public static bool IsValidBST(BinaryNode root) { List<int> nodes = new List<int>(); subIsValidBST(root, nodes); if (nodes.Count > 1) return false; else return true; } private static void subIsValidBST(BinaryNode root,List<int>nodes) { if (root != null) { subIsValidBST(root.left, nodes); nodes.Add(root.val); if (nodes.Count > 1) { if (nodes[1] <= nodes[0]) return; else nodes.RemoveAt(0); } subIsValidBST(root.right, nodes); } }isValidBST
类似地,如果按照右节点-根节点-左节点的顺序输出,则会得到降序序列。
此外,中序遍历中还有个莫里斯遍历。其特点是不使用栈或递归,空间复杂度是常数。我感觉这个方法的核心思想在于对于没有右子树的节点,通过把右子树指向根节点来代替使用栈。但是因为这样会造成循环,因此需要遍历两边,第二遍把右子树到根节点的指向取消。
需要使用两个指针tmp和curNode。
def MorrisTraverse(self,root): nodes=[] curNode,tmp=root,None while curNode: if curNode.left: tmp=curNode.left while tmp.right and tmp.right!=curNode: tmp=tmp.right if tmp.right is None: tmp.right=curNode curNode=curNode.left else: nodes.append(curNode.val) tmp.right=None curNode=curNode.right else: nodes.append(curNode.val) curNode=curNode.right return nodesMorrisTraverse
(四)后序遍历:左节点-右节点-根节点(即输出为:1,2,4,6,5,3,13,16,15,21,30,29,20,8)
public static List<int>postOrderTraverse(BinaryNode root) { List<int> nodes = new List<int>(); subPostOrderTraverse(root, nodes); return nodes; } private static void subPostOrderTraverse(BinaryNode root, List<int> nodes) { if(root != null) { subPostOrderTraverse(root.left, nodes); subPostOrderTraverse(root.right, nodes); nodes.Add(root.val); } }postOrderTraverse
(五)广度优先搜索(breath first search, BFS),也叫作层次遍历,输出为:8,3,20,2,5,15,29,1,4,6,13,16,21,30。使用队列实现。
public static List<int>breathFirstSearch(BinaryNode root) { QueuebreathFirstSearchqueue = new Queue (); queue.Enqueue(root); List<int> nodes = new List<int>(); while(queue.Count != 0) { BinaryNode node = queue.Dequeue(); nodes.Add(node.val); if (node.left != null) queue.Enqueue(node.left); if (node.right != null) queue.Enqueue(node.right); } return nodes; }
根据BSF的特点,可以实现二叉树的锯齿形层次遍历。leetcode103:给定一个二叉树,返回其节点值的锯齿形层序遍历,即先从左往右,再从右往左进行下一层遍历。我用的方法如下:
public static ListzigzagLevelOrderint>>zigzagLevelOrder(BinaryNode root) { List
int>> nodes = new List
int>>(); Queue
queue = new Queue (); queue.Enqueue(root); int length = queue.Count; while (queue.Count != 0) { List<int> _nodes = new List<int>(); while (length > 0) { BinaryNode node = queue.Dequeue(); _nodes.Add(node.val); if (node.left != null) queue.Enqueue(node.left); if (node.right != null) queue.Enqueue(node.right); length--; } if (nodes.Count == 0) nodes.Add(_nodes); else if(nodes.Count ==1) { _nodes.Reverse(); nodes.Add(_nodes); } else { if (nodes.Count % 2 == 0) nodes.Add(_nodes); else { _nodes.Reverse(); nodes.Add(_nodes); } } length = queue.Count; } return nodes; }
(六)二叉树的子树高度问题
leetcode平衡二叉树问题:给定一个二叉树,判断它是否是高度平衡的二叉树。高度平衡意味着一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1.此问题可以转化为二叉树的子树高度问题。
public static bool isBalanced(BinaryNode node) { if (node == null) return true; if (Math.Abs(subTreeHeight(node.left) - subTreeHeight(node.right)) > 1) return false; return isBalanced(node.left) && isBalanced(node.right); } private static int subTreeHeight(BinaryNode node) { if (node == null) return 0; else { return Math.Max(subTreeHeight(node.left), subTreeHeight(node.right)) + 1; } }binaryTreeHeight
类似地,还有求二叉树的最小深度问题,最小深度是从根节点到最近叶子节点的最短路径上的节点数量。例如root=[3,9,20,null,null,15,7],最小深度为2,即3-9两个节点。
public static int MinDepth(BinaryNode node) { if (node == null) return 0; if (node.left == null && node.right != null) return MinDepth(node.right) + 1; if (node.right == null && node.left != null) return MinDepth(node.left) + 1; return Math.Min(MinDepth(node.left) , MinDepth(node.right)) + 1; }minDpeth