【数据结构】——二叉树
目录
一.深度优先遍历DFS(前中后序遍历)
1.先序遍历(中左右)
2.中序遍历(左中右)——>对于搜索树来说是递增序列
3.后序遍历(左右中)——>可以处理深度的问题(栈的最大高度就是树的最大深度)
4.如何走完整棵树
二.层序遍历BFS
1.层序遍历
2.Z字形遍历(无非就是奇偶数反一下)
二.最大深度——后序遍历
1.递归法
2.非递归(栈的最大深度就是树的最大高度)
3.层序遍历
三.最小深度
1.递归(后序遍历)
2.层序遍历
四.二叉树的右视图
五.二叉搜索树的最近公共祖先
六.判断是否为对称二叉树
七.二叉树的翻转
八.寻找二叉搜索树中的目标节点
九.二叉搜索树范围查询
十.范围和
1.中序遍历
2.递归解
一.深度优先遍历DFS(前中后序遍历)
1.先序遍历(中左右)
先打印出当前节点,然后往左走到底,走到底后再往右走。(走哪打哪)
2.中序遍历(左中右)——>对于搜索树来说是递增序列
往右走之前,或者就没有右子树时,打印当前节点。(左边走完再打)
3.后序遍历(左右中)——>可以处理深度的问题(栈的最大高度就是树的最大深度)
右子树处理完成了,也就是弹栈了,打印弹栈节点。(右边走完再打)
4.如何走完整棵树
问题1:为什么要压栈?因为要记住来时的路,为什么要记住来时的路,因为当前节点还没处理完
一开始一直往左走,直到为空,要回头,怎么回头?有栈记住了来时的路——stack.peek()
问题2:开始处理右子树:怎么处理右子树?往右走?怎么知道右子树处理完了?
1.拿到的这个栈顶元素就没有右子树,当然就算处理完了,pop掉就完了
2.如果有右子树,栈顶元素的右孩子等于刚刚弹栈的元素,也就知道右子树也处理完了。pop掉
代码演示
LinkedList<TreeNode> stack = new LinkedList<>();TreeNode curr = root; // 代表当前节点TreeNode pop = null; // 最近一次弹栈的元素while (!stack.isEmpty() || curr != null) {if (curr != null) {stack.push(curr);// 待处理左子树colorPrintln("前:" + curr.val, 31);curr = curr.left;} else {TreeNode peek = stack.peek();// 没有右子树if (peek.right == null) {colorPrintln("中:" + peek.val, 36);pop = stack.pop();colorPrintln("后:" + pop.val, 34);}// 右子树处理完成else if (peek.right == pop) {pop = stack.pop();colorPrintln("后:" + pop.val, 34);}// 待处理右子树else {colorPrintln("中:" + peek.val, 36);curr = peek.right;}}}
二.层序遍历BFS
1.层序遍历
每一层都可以用一个队列来存储
public List<List<Integer>> levelOrder1(TreeNode root) {List<List<Integer>> result = new ArrayList<>();LinkedList<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(root);while (!queue.isEmpty()) {ArrayList<Integer> list = new ArrayList<>();int size = queue.size();for (int i = 0; i < size; i++) {TreeNode n = queue.poll();list.add(n.val);if (n.left != null){queue.offer(n.left);}if (n.right != null){queue.offer(n.right);}}result.add(list);}return result;}
2.Z字形遍历(无非就是奇偶数反一下)
public List<List<Integer>> ZlevelOrder(TreeNode root) {List<List<Integer>> result = new ArrayList<>();LinkedList<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(root);boolean b = true;while (!queue.isEmpty()) {LinkedList<Integer> list = new LinkedList<Integer>();int size = queue.size();for (int i = 0; i < size; i++) {TreeNode n = queue.poll();if (b) {list.offerLast(n.val);}else {list.offerFirst(n.val);}if (n.left != null) {queue.offer(n.left);}if (n.right != null) {queue.offer(n.right);}}result.add(list);b = !b;}return result;}
二.最大深度——后序遍历
1.递归法
public int maxDepth(TreeNode node) {if (node == null) {return 0;}/*if (node.left == null && node.right == null) {return 1;}*/int d1 = maxDepth(node.left);int d2 = maxDepth(node.right);return Integer.max(d1, d2) + 1;}
2.非递归(栈的最大深度就是树的最大高度)
public int maxDepth(TreeNode root) {TreeNode curr = root;TreeNode pop = null;LinkedList<TreeNode> stack = new LinkedList<>();int max = 0; // 栈的最大高度while (curr != null || !stack.isEmpty()) {if (curr != null) {stack.push(curr);int size = stack.size();if (size > max) {max = size;}curr = curr.left;} else {TreeNode peek = stack.peek();if (peek.right == null || peek.right == pop) {pop = stack.pop();} else {curr = peek.right;}}}return max;}
3.层序遍历
public int maxDepth(TreeNode root) {if (root == null) {return 0;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);int depth = 0;while (!queue.isEmpty()) {int size = queue.size();for (int i = 0; i < size; i++) {TreeNode poll = queue.poll();
// System.out.print(poll.val + "\t");if (poll.left != null) {queue.offer(poll.left);}if (poll.right != null) {queue.offer(poll.right);}}
// System.out.println();depth ++;}return depth;}
三.最小深度
1.递归(后序遍历)
public int minDepth(TreeNode node) {if (node == null) {return 0;}int d1 = minDepth(node.left); // 1int d2 = minDepth(node.right); // 0if (d2 == 0) { // 当右子树为nullreturn d1 + 1; // 返回左子树深度+1}if (d1 == 0) { // 当左子树为nullreturn d2 + 1; // 返回右子树深度+1}return Integer.min(d1, d2) + 1;}
2.层序遍历
public int minDepth(TreeNode root) {if (root == null) {return 0;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);int depth = 0;while (!queue.isEmpty()) {int size = queue.size();depth ++;for (int i = 0; i < size; i++) {TreeNode poll = queue.poll();if (poll.left == null && poll.right == null) {return depth;}if (poll.left != null) {queue.offer(poll.left);}if (poll.right != null) {queue.offer(poll.right);}}}return depth;}
四.二叉树的右视图
public List<Integer> rightSideView(TreeNode root) {Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);List<Integer> list = new ArrayList<>();if(root == null) return list;while (!queue.isEmpty()){int size = queue.size();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();if (node.left != null){queue.offer(node.left);}if (node.right != null){queue.offer(node.right);}if (i == size - 1){list.add(node.val);}}}return list;}
五.二叉搜索树的最近公共祖先
/*__ 6 __/ \2 8/ \ / \0 4 7 9/ \3 5待查找节点 p q 在某一节点的两侧,那么此节点就是最近的公共祖先*/public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {TreeNode a = root;while (p.val < a.val && q.val < a.val || a.val < p.val && a.val < q.val) { // 在同一侧if (p.val < a.val) {a = a.left;} else {a = a.right;}}return a;}
六.判断是否为对称二叉树
public boolean isSymmetric(TreeNode root) {return check(root.left, root.right);}private boolean check(TreeNode left, TreeNode right) {if (left == null && right == null) {return true;}// left 和 right 不能同时为 nullif(right == null || left == null) {return false;}if (left.val != right.val) {return false;}return check(left.left, right.right) && check(left.right, right.left);}
七.二叉树的翻转
public TreeNode invertTree(TreeNode root) {fn(root);return root;}private static void fn(TreeNode node) {if (node == null) {return;}TreeNode t = node.left;node.left = node.right;node.right = t;fn(node.left);fn(node.right);}
八.寻找二叉搜索树中的目标节点
中序遍历可以得到一个二叉搜索树的递增序列,所以使用中序遍历。
class Solution {int index = 0;int res;public int findTargetNode(TreeNode root, int cnt) {dfs(root, cnt);return res;}public void dfs(TreeNode root, int cnt){if(root == null) return;dfs(root.right, cnt);index++;if(index > cnt) return;if(index == cnt) res = root.val;dfs(root.left, cnt);}
}
九.二叉搜索树范围查询
同理也是中序遍历,然后在打印的位置加上if条件
十.范围和
1.中序遍历
public int rangeSumBST2(TreeNode root, int low, int high){LinkedList<TreeNode> stack = new LinkedList<>();TreeNode curr = root;TreeNode pop = null;int sum = 0;while(!stack.isEmpty() || curr != null){if (curr != null) {stack.push(curr);curr = curr.left;}else {//处理右子树TreeNode peek = stack.peek();if (peek.right == null){pop = stack.pop();if (pop.val > high) {break;}if (pop.val >= low){sum += pop.val;}}else if (peek.right == pop){pop = stack.pop();}else {if (peek.val > high) {break;}if (peek.val >= low){sum += peek.val;}curr = peek.right;}}}return sum;}
2.递归解
public int rangeSumBST(TreeNode node, int low, int high) {if (node == null) {return 0;}if (node.val < low) {return rangeSumBST(node.right, low, high);}if (node.val > high) {return rangeSumBST(node.left, low, high);}// 在范围内return node.val + rangeSumBST(node.left, low, high) + rangeSumBST(node.right, low, high);}