递归方式需要理解递归序
先序遍历
任何子树的处理顺序都是,先头结点、再左子树、然后右子树
public static void pre(Node head) {if (head == null) {return;}System.out.println(head.value);pre(head.left);pre(head.right);}
public static void pre(Node head) {System.out.print("pre-order: ");if (head != null) {Stack<Node> stack = new Stack<Node>();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();}
中序遍历
任何子树的处理顺序都是,先左子树、再头结点、然后右子树
public static void in(Node head) {if (head == null) {return;}in(head.left);System.out.println(head.value);in(head.right);}
public static void in(Node cur) {System.out.print("in-order: ");if (cur != null) {Stack<Node> stack = new Stack<Node>();while (!stack.isEmpty() || cur != null) {if (cur != null) {stack.push(cur);cur = cur.left;} else {cur = stack.pop();System.out.print(cur.value + " ");cur = cur.right;}}}System.out.println();}
后续遍历
任何子树的处理顺序都是,先左子树、再右子树、然后头结点
public static void pos(Node head) {if (head == null) {return;}pos(head.left);pos(head.right);System.out.println(head.value);}
public static void pos1(Node head) {System.out.print("pos-order: ");if (head != null) {//利用栈的来逆序 头右左 -> 左右头Stack<Node> s1 = new Stack<Node>();Stack<Node> s2 = new Stack<Node>();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();}
public static void pos2(Node h) {System.out.println("pos-order: ");if (h != null) {Stack<Node> stack = new Stack<>();stack.push(h);Node c = null;while (!stack.isEmpty()) {c = stack.peek();if (c.left != null && h != c.left && h != c.right) {stack.push(c.left);} else if (c.right != null && h != c.right) {stack.push(c.right);}else {System.out.print(stack.pop().value + " ");h = c;}}}System.out.println();}
加深理解
(证明)在二叉树中给定一个节点X,先序遍历中X左边的节点集合与后续遍历中X右边节点集合的交集一定都是X节点的祖先节点
1、X的孩子节点不会出现在交集中 先序:头 X 左右 后续:左右 X 头 2、X作为左树姿态下右兄节点不会出现在先序遍历中X左侧节点集合 先序:头左右 X 3、X作为右树姿态下左兄节点不会出现在后序遍历中X右侧侧节点集合 后续:左右头 X
宽度优先遍历
public static void level(Node head) {if (head == null) {return;}Queue<Node> queue = new LinkedList<>();queue.add(head);while (!queue.isEmpty()) {Node curr = queue.poll();System.out.println(curr.value);if (curr.left != null) {queue.add(curr.left);}if (curr.right != null) {queue.add(curr.right);}}}
给定一颗二叉树,输出最宽层的宽度
//思路,遍历过程中记录每个节点的层数是第几层public static int maxWidthUseMap(Node head) {if (head == null) {return 0;}Queue<Node> queue = new LinkedList<>();queue.add(head);// key 在 哪一层,valueHashMap<Node, Integer> levelMap = new HashMap<>();levelMap.put(head, 1);int curLevel = 1; // 当前你正在统计哪一层的宽度int curLevelNodes = 0; // 当前层curLevel层,宽度目前是多少int max = 0;while (!queue.isEmpty()) {Node cur = queue.poll();int curNodeLevel = levelMap.get(cur);if (cur.left != null) {levelMap.put(cur.left, curNodeLevel + 1);queue.add(cur.left);}if (cur.right != null) {levelMap.put(cur.right, curNodeLevel + 1);queue.add(cur.right);}if (curNodeLevel == curLevel) {curLevelNodes++;} else {max = Math.max(max, curLevelNodes);curLevel++;curLevelNodes = 1;}}max = Math.max(max, curLevelNodes);return max;}
public static int maxWidthNoMap(Node head) {if (head == null) {return 0;}Queue<Node> queue = new LinkedList<>();queue.add(head);Node curEnd = head; // 当前层,最右节点是谁Node nextEnd = null; // 下一层,最右节点是谁int max = 0;int curLevelNodes = 0; // 当前层的节点数while (!queue.isEmpty()) {Node cur = queue.poll();if (cur.left != null) {queue.add(cur.left);nextEnd = cur.left;}if (cur.right != null) {queue.add(cur.right);nextEnd = cur.right;}curLevelNodes++;if (cur == curEnd) {max = Math.max(max, curLevelNodes);curLevelNodes = 0;curEnd = nextEnd;}}return max;}
二叉树的序列化与反序列化
二叉树可以通过先序、后序或者按层遍历的方式序列化和反序列化,
但是,二叉树无法通过中序遍历的方式实现序列化和反序列化
因为不同的两棵树,可能得到同样的中序序列,即便补了空位置也可能一样。
比如如下两棵树
2
/
1
和
1
\
2
补足空位置的中序遍历结果都是{ null, 1, null, 2, null}
//序列化public static Queue<String> preSerial(Node head) {Queue<String> ans = new LinkedList<>();pres(head, ans);return ans;}public static void pres(Node head, Queue<String> ans) {if (head == null) {ans.add(null);} else {ans.add(String.valueOf(head.value));pres(head.left, ans);pres(head.right, ans);}}//反序列化public static Node buildByPreQueue(Queue<String> preList) {if (preList == null || preList.isEmpty()) {return null;}return preb(preList);}private static Node preb(Queue<String> preList) {String val = preList.poll();if (val == null) {return null;}Node node = new Node(Integer.valueOf(val));node.left = preb(preList);node.right = preb(preList);return node;}
//序列化public static Queue<String> posSerial(Node head) {Queue<String> ans = new LinkedList<>();poss(head, ans);return ans;}public static void poss(Node head, Queue<String> ans) {if (head == null) {ans.add(null);} else {poss(head.left, ans);poss(head.right, ans);ans.add(String.valueOf(head.value));}}//反序列化public static Node buildByPosQueue(Queue<String> posList) {if (posList == null || posList.isEmpty()) {return null;}//后序遍历 左右头 使用栈逆序后变成 头左右,参考后序遍历非递归实现Stack<String> stack = new Stack<>();while (!posList.isEmpty()) {stack.push(posList.poll());}return posb(stack);}private static Node posb(Stack<String> stack) {String val = stack.pop();if (val == null) {return null;}Node node = new Node(Integer.valueOf(val));node.right = posb(stack);node.left = posb(stack);return node;}
//序列化public static Queue<String> levelSerial(Node head) {Queue<String> ans = new LinkedList<>();if (head == null) {ans.add(null);} else {ans.add(String.valueOf(head.value));Queue<Node> queue = new LinkedList<>();queue.add(head);while (!queue.isEmpty()) {head = queue.poll(); // head 父 子if (head.left != null) {ans.add(String.valueOf(head.left.value));queue.add(head.left);} else {ans.add(null);}if (head.right != null) {ans.add(String.valueOf(head.right.value));queue.add(head.right);} else {ans.add(null);}}}return ans;}//反序列化public static Node buildByLevelQueue(Queue<String> levelList) {if (levelList == null || levelList.size() == 0) {return null;}Node head = generateNode(levelList.poll());Queue<Node> queue = new LinkedList<Node>();if (head != null) {queue.add(head);}Node node = null;while (!queue.isEmpty()) {node = queue.poll();node.left = generateNode(levelList.poll());node.right = generateNode(levelList.poll());if (node.left != null) {queue.add(node.left);}if (node.right != null) {queue.add(node.right);}}return head;}public static Node generateNode(String val) {if (val == null) {return null;}return new Node(Integer.valueOf(val));}
将N叉树使用二叉树编码和反编码
将多叉树的第一个孩子节点作为二叉树的左子树头结点,其他孩子依次作为第一个孩子节点的又节点 如下:②③④为多叉树中①的孩子节点,⑤⑥为多叉树中②的孩子节点(长兄为父) ① / ② / \ ⑤ ③ \ \ ⑥ ④
//N叉树public static class Node {public int val;public List<Node> children;public Node() {}public Node(int _val) {val = _val;}public Node(int _val, List<Node> _children) {val = _val;children = _children;}};//二叉树public static class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}}//将多叉树转为二叉树public TreeNode encode(Node root) {if (root == null) {return null;}TreeNode head = new TreeNode(root.val);head.left = en(root.children);return head;}private TreeNode en(List<Node> children) {TreeNode head = null;TreeNode cur = null;for (Node child : children) {TreeNode tNode = new TreeNode(child.val);if (head == null) {head = tNode;} else {cur.right = tNode;}cur = tNode;cur.left = en(child.children);}return head;}//将二叉树转为多叉树public Node decode(TreeNode root) {if (root == null) {return null;}return new Node(root.val, de(root.left));}public List<Node> de(TreeNode root) {List<Node> children = new ArrayList<>();while (root != null) {children.add(new Node(root.val, de(root.left)));root = root.right;}return children;}
给定二叉树中的某个节点,返回该节点的后继节点
二叉树的定义如下: class Node { V value; Node left; Node right; Node parent; }
后继节点:在二叉树中序遍历中的下一个节点
public static Node getSuccessorNode(Node node) {if (node == null) {return node;}if (node.right != null) {//如果有右子树,那后继节点为右子树上最左节点return getLeftMost(node.right);} else { // 无右子树//寻找该节点第一次出现在哪个父亲节点的左子树上,则该父亲节点即为此节点的后继节点Node parent = node.parent;while (parent != null && parent.right == node) { // 当前节点是其父亲节点右孩子node = parent;parent = node.parent;}return parent;}}public static Node getLeftMost(Node node) {if (node == null) {return node;}while (node.left != null) {node = node.left;}return node;}
如何设计一个打印正科树的打印函数
请把一张纸条竖着放在桌子上,然后从纸条的下方向上方对折1次,压出折痕后展开。此时折痕是凹下去的,即折痕凸起的方向指向纸条的背面。如果从纸条的下边向上方连续对折2吃,压出折痕后展开,此时有三条折痕,从上到下一次是下折痕、下折痕和上折痕。给定一个输入参数N,代表纸条都从下边向上方连续对折N次。请从上到下打印所有折痕的方向。例如:N=1时,打印:dwon;N=2时,打印:down down up
//该题的规律,折痕最终组成一颗二叉树,折的次数代表二叉树有几层//该二叉树中,头结点是凹折痕,之后任何节点的左节点都是凹,任何节点的右节点都是凸public static void printAllFolds(int N) {process(1, N, true);System.out.println();}// 当前你来了一个节点,脑海中想象的!// 这个节点在第i层,一共有N层,N固定不变的// 这个节点如果是凹的话,down = T// 这个节点如果是凸的话,down = F// 函数的功能:中序打印以你想象的节点为头的整棵树!public static void process(int i, int N, boolean down) {if (i > N) {return;}process(i + 1, N, true);System.out.print(down ? "凹 " : "凸 ");process(i + 1, N, false);}


对数器















