简介
二叉树有两种存储方式,分别是顺序存储和链式存储。遍历算法的作用就是讲非线性结构转为线性结构,分为深度优先遍历和广度优先遍历。
深度优先遍历
深度优先遍历算法又分为三种,分别是前序遍历,中序遍历和后序遍历。这个前中后序指的是遍历根节点的顺序。依照下图:解决三种遍历方式的算法都可以用递归或者迭代法去实现,在二叉树或者链表的算法问题中,推荐使用递归去解决问题,当然递归的问题是思考起来会难一点,不易形成思路。迭代法的好处是符合正常思维,考虑起来简单,代码量会多一点。
- 前序遍历:ABCDEFGHK
- 中序遍历:BDCAEHGKF
- 后续遍历:DCBHKGFEA
前序遍历
根结点——> 左节点——>右节点
ABCDEFGHK
/**
* L144 二叉树的前序遍历
*
* 迭代法
*
* @param root
* @return
*/
public List<Integer> preorderTraversal2(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curNode = root;
while (curNode != null || !stack.isEmpty()) {
while (curNode != null) {
//沿途的值全部添加到结果,
result.add(curNode.val);
//查到最左边的子节点,沿途经过的节点全部压栈,后面用。
stack.push(curNode);
curNode = curNode.left;
}
//左边无节点的话,找到上一个节点的右节点,继续判断即可。
curNode = stack.pop().right;
}
return result;
}
/**
* L144 二叉树的前序遍历
*
* 递归
*
* @param root
* @return
*/
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
preorderTraversal(root, result);
return result;
}
private void preorderTraversal(TreeNode root, List<Integer> result) {
// 添加根节点的值
result.add(root.val);
//添加左节点的值
if (root.left != null) {
preorderTraversal(root.left, result);
}
//添加右节点的值
if (root.right != null) {
preorderTraversal(root.right, result);
}
}
中序遍历
左结点——> 根节点——>右节点
BDCAEHGKF
/**
* L94 二叉树的中序遍历
*
* 循环法:空间复杂度o(n), 时间复杂度o(n)
*
* @param root
* @return
*/
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<> ();
TreeNode curNode = root;
while(!stack.isEmpty() || curNode != null) {
while (curNode != null) {
stack.push(curNode);
curNode = curNode.left;
}
curNode = stack.pop();
result.add(curNode.val);
curNode = curNode.right;
}
return result;
}
/**
* L94 二叉树的中序遍历
* 递归法 时间复杂度o(n), 空间复杂度最坏o(n)
*
* @param root
* @return
*/
public List<Integer> inorderTraversal2(TreeNode root) {
List<Integer> result = new ArrayList<>();
traversal(root, result);
return result;
}
private void traversal(TreeNode root, List<Integer> result) {
if (root.left != null) {
result.addAll(inorderTraversal2(root.left));
}
result.add(root.val);
if (root.right != null) {
result.addAll(inorderTraversal2(root.right));
}
}
后序遍历
左结点——> 右节点——>根节点
DCBHKGFEA
/**
* L145 二叉树的后序遍历
*
* @param root
* @return
*/
public List<Integer> postorderTraversal2(TreeNode root) {
LinkedList<Integer> result = new LinkedList<>();
if (root == null) {
return result;
}
LinkedList<TreeNode> stack = new LinkedList<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pollLast();
result.addFirst(node.val);
if (node.left != null) {
stack.add(node.left);
}
if (node.right != null) {
stack.add(node.right);
}
}
return result;
}
/**
* L145 二叉树的后序遍历
*
* @param root
* @return
*/
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
postorderTraversal(root, result);
return result;
}
public void postorderTraversal(TreeNode root, List<Integer> result) {
if (root.left != null) {
postorderTraversal(root.left, result);
}
if (root.right != null) {
postorderTraversal(root.right, result);
}
result.add(root.val);
}
广度优先搜索
/**
* 广度优先遍历
*/
public List<Integer> breadthFirst(TreeNode root) {
List<Integer> result = new ArrayList<>();
LinkedList<TreeNode> linkedList = new LinkedList<>();
linkedList.offer(root);
result.add(root.val);
while (!linkedList.isEmpty()) {
TreeNode top = linkedList.poll();
if (top.left != null) {
linkedList.offer(top.left);
result.add(top.left.val);
}
if (top.right != null) {
linkedList.offer(top.right);
result.add(top.right.val);
}
}
return result;
}
总结
二叉树因其特性,子树也是个二叉树。所以很多问题,都可以通过递归的方式解决。