算法基础入门——二叉树递归遍历与非递归遍历、二叉树最大宽度、判断是否是二叉搜索树、判断是否是完全二叉树、判断是否是平衡二叉树、最低公共祖先节点、获取某一节点的后继节点、对折纸张的折痕问题
package com.zuoshen.jichurumen.class05; import java.util.*; /** * @author ShiZhe * @create 2022-02-28 20:41 */ public class code01 { /** * 二叉树节点 */ public static class Node { public int value; public Node left; public Node right; public Node(int data) { this.value = data; } } /** * 二叉树前序递归遍历 * @param head */ public static void preOrderRecur(Node head) { if (head == null) { return; } System.out.print(head.value + " "); preOrderRecur(head.left); preOrderRecur(head.right); } /** * 二叉树前序非递归遍历 * 通过栈来实现 * 先右后左 * @param head */ public static void preOrderUnRecur(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(); } /** * 二叉树中序递归遍历 * @param head */ public static void inOrderRecur(Node head) { if (head == null) { return; } inOrderRecur(head.left); System.out.print(head.value + " "); inOrderRecur(head.right); } /** * 二叉树中序非递归遍历 * 通过栈来实现 * 先放左,再放右 * @param head */ public static void inOrderUnRecur(Node head) { System.out.print("in-order: "); if (head != null) { Stack<Node> 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(); } /** * 二叉树后序递归遍历 * @param head */ public static void posOrderRecur(Node head) { if (head == null) { return; } posOrderRecur(head.left); posOrderRecur(head.right); System.out.print(head.value + " "); } /** * 二叉树后序递归遍历-1 * 与二叉树前序遍历类似 * @param head */ public static void posOrderUnRecur1(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(); } /** * 二叉树后序递归遍历-2 * 利用栈的peek()函数获取栈顶的节点 * 我们什么时候才能访问节点。有如下两种情况: * 当前经过节点是叶子节点。 * 当前经过节点的右子节点是上一次访问的节点。 * @param head */ public static void posOrderUnRecur2(Node head) { System.out.print("pos-order: "); Stack<Node> stack = new Stack<>(); // 当前节点 Node cur = head; // 用于记录上一次访问的节点 Node pre = null; while (cur != null || !stack.isEmpty()) { while (cur != null) { stack.push(cur); cur = cur.left; } if (!stack.isEmpty()) { cur = stack.pop(); // 访问节点的调节 if (cur.right == null || pre == cur.right) { // 访问 System.out.print(cur.value + " "); // 这一步是记录上一次访问的节点 pre = cur; // 此处为了跳过下一次循环的访问左子节点的过程,直接进入栈的弹出阶段,因为但凡在栈中的节点,它们的左子节点都肯定被经过且已放入栈中。 cur = null; } else { // 不访问节点的条件 // 将已弹出的根节点放回栈中 stack.push(cur); // 经过右子节点 cur= cur.right; } } } System.out.println(); } /** * 二叉树最大宽度 * 使用队列 * @param head */ public static int getMaxWidth(Node head) { if (head == null) { return 0; } // hashMap将节点与层数绑定,通过层数的变化来计算每一层的width HashMap<Node, Integer> levelMap = new HashMap<>(); // queue存储每层的节点 // linkedList与arrayList的区别在于linkedList使用双链表存储,而arrayList使用数组存储。 LinkedList<Node> queue = new LinkedList<>(); // 需要求的最大的width int maxWidth = 0; // 当前层的width int curWidth = 0; // 当前层数 int curLevel = 1; // 初始化 levelMap.put(head, 1); queue.add(head); Node node = null; Node left = null; Node right = null; while (!queue.isEmpty()) { node = queue.poll(); left = node.left; right = node.right; if (left != null) { levelMap.put(left, levelMap.get(node) + 1); queue.add(left); } if (right != null) { levelMap.put(right, levelMap.get(node) + 1); queue.add(right); } if (levelMap.get(node) > curLevel) { curWidth = 1; curLevel = levelMap.get(node); } else { curWidth++; } maxWidth = Math.max(curWidth, maxWidth); } return maxWidth; } /** * 判断是否是二叉搜索树,中序遍历是有序的。 * @param head * @return */ public static boolean isBST(Node head) { if (head == null) { return true; } LinkedList<Node> inOrderList = new LinkedList<>(); isBSTProcess(head, inOrderList); int pre = Integer.MIN_VALUE; Node cur = null; while (!inOrderList.isEmpty()) { cur = inOrderList.poll(); if (pre >= cur.value) { return false; } pre = cur.value; } return true; } /** * BST的中序遍历 * @param node * @param inOrderList */ public static void isBSTProcess(Node node, LinkedList<Node> inOrderList) { if (node == null) { return; } isBSTProcess(node.left, inOrderList); inOrderList.add(node); isBSTProcess(node.right, inOrderList); } /** * CBT:完全二叉树,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同, * 则这棵二叉树称为完全二叉树。 * 叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。 * @param head * @return */ public static boolean isCBT(Node head) { if (head == null) { return true; } LinkedList<Node> queue = new LinkedList<>(); // 判断是否为叶子结点 boolean leaf = false; queue.add(head); Node cur = null; while (!queue.isEmpty()) { cur = queue.poll(); // 左无右有返回false // 已经出现叶子节点了,后面出现非叶子节点的返回false if ((cur.left == null && cur.right!=null) || (leaf && (cur.left != null ||cur.right != null))) { return false; } if (cur.left != null) { queue.add(cur.left); } // 右边为空,则后面均为叶子结点 if (cur.right != null) { queue.add(cur.right); } else { leaf = true; } } return true; } /** * 判断是否是平衡二叉树:左右子树的高度差的绝对值不超过1 * @param head * @return */ public static boolean isBalanced(Node head) { return process(head).isBalanced; } /** * 平衡二叉树的返回数据格式 */ public static class ReturnTypeIsBalanced { // 当前数的高度 public int height; // 当前数是否是平衡二叉树 public boolean isBalanced; // 构造函数 public ReturnTypeIsBalanced(boolean isBalanced, int height) { this.height = height; this.isBalanced = isBalanced; } } /** * 判断是否是平衡二叉树的处理函数 * 向左数要数据,向右数要数据,比较,得到答案 * @param head * @return */ public static ReturnTypeIsBalanced process(Node head) { if (head == null) { return new ReturnTypeIsBalanced(true, 0); } ReturnTypeIsBalanced leftData = process(head.left); ReturnTypeIsBalanced rightData = process(head.right); int height = Math.max(leftData.height, rightData.height) + 1; boolean isBalanced = leftData.isBalanced && rightData.isBalanced && Math.abs(leftData.height - rightData.height) < 2; return new ReturnTypeIsBalanced(isBalanced, height); } /** * 最低公共祖先节点 * 递归遍历,寻找节点 * @param head * @param o1 * @param o2 * @return */ public static Node lowestAncestor(Node head, Node o1, Node o2) { // 发现目标节点,标记返回 if (head == null || head == o1 || head == o2) { return head; } // 在左子树上找寻目标节点 Node left = lowestAncestor(head.left, o1, o2); // 在右子树找寻目标节点 Node right = lowestAncestor(head.right, o1, o2); // 在左子树和右子树上分别找到,则改树的头为最低公共祖先节点 if (left != null && right != null) { return head; } // 有一个为空,则表示2个在同侧,返回非空节点就行 return left != null ? left : right; } /** * 拥有parent指针的二叉树节点结构 */ public static class NodeNew { public int value; public NodeNew left; public NodeNew right; public NodeNew parent; public NodeNew(int data) { this.value = data; } } /** * 获取某一节点的后继节点 * 在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。 * @param node * @return */ public static NodeNew getSuccessorNode(NodeNew node) { if (node == null) { return node; } // 右子树不为空,后继节点为右子树最左节点 if (node.right != null) { return getLeftMost(node.right); } else { // 右子树为空 // 当该节点为父亲节点的左子树时,该节点的后继节点就是父亲节点 // 当该节点不是父亲节点的左子树的,将父亲节点设为子节点,循环找子节点为父亲节点的左子树的父亲节点 NodeNew parent = node.parent; while (parent.left != node && parent != null) { node = parent; parent = node.parent; } return parent; } } /** * 获取该节点最左节点 * @param node * @return */ public static NodeNew getLeftMost(NodeNew node) { if (node.left == null) { return node; } while (node.left != null) { node = node.left; } return node; } /** * 按照先序遍历序列化二叉树 * @param head * @return */ public static String serialByPre(Node head) { if (head == null) { return "#!"; } String res = head.value + "!"; res += serialByPre(head.left); res += serialByPre(head.right); return res; } /** * 二叉树的反序列化 * @param preStr * @return */ public static Node reconByPreString(String preStr) { // 将value按照!划分出来 String[] values = preStr.split("!"); LinkedList<String> queue = new LinkedList<String>(); // 放入队列 for (int i = 0; i != values.length; i++) { queue.offer(values[i]); } return reconPreOrder(queue); } /** * 递归调用 * @param queue * @return */ public static Node reconPreOrder(LinkedList<String> queue) { String value = queue.poll(); // #表示空 if (value.equals("#")) { return null; } Node head = new Node(Integer.valueOf(value)); head.left = reconPreOrder(queue); head.right = reconPreOrder(queue); return head; } /** * 对折纸张的折痕问题 * 看做一个中序遍历二叉树的问题 * @param N */ public static void printAllFolds(int N) { printProcess(1, N, true); } /** * 题意是从上至下,二叉树就需要先右后左 * 右是下 down,左是上 up * @param i 当前层数,也是当前次数 * @param N 总层数,也是总次数 * @param down */ public static void printProcess(int i, int N, boolean down) { if (i > N) { return; } printProcess(i + 1, N, true); System.out.println(down ? "down " : "up "); printProcess(i + 1, N, false); } public static void main(String[] args) { Node head = new Node(5); head.left = new Node(3); head.right = new Node(8); head.left.left = new Node(2); Node node2 = new Node(4); head.left.right = node2; Node node1 = new Node(1); head.left.left.left = node1; head.right.left = new Node(7); head.right.left.left = new Node(6); head.right.right = new Node(10); head.right.right.left = new Node(9); head.right.right.right = new Node(11); head.left.right.left = new Node(12); head.left.right.right = new Node(13); head.left.left.right = new Node(14); Node head1 = new Node(9); head1.left = new Node(3); head1.left.left = new Node(4); // recursive System.out.println("==============recursive=============="); System.out.print("pre-order: "); preOrderRecur(head); System.out.println(); System.out.print("in-order: "); inOrderRecur(head); System.out.println(); System.out.print("pos-order: "); posOrderRecur(head); System.out.println(); // unrecursive System.out.println("============unrecursive============="); preOrderUnRecur(head); inOrderUnRecur(head); posOrderUnRecur1(head); posOrderUnRecur2(head); // maxWidth int maxWidth = getMaxWidth(head); System.out.println(maxWidth); // isBST boolean bst = isBST(head); System.out.println(bst); // isBalanced boolean balanced = isBalanced(head); System.out.println(balanced); boolean balanced1 = isBalanced(head1); System.out.println(balanced1); // 最低公共祖先 Node node = lowestAncestor(head, node1, node2); System.out.println(node.value); // 后继节点 NodeNew head2 = new NodeNew(6); head2.parent = null; head2.left = new NodeNew(3); head2.left.parent = head2; head2.left.left = new NodeNew(1); head2.left.left.parent = head2.left; head2.left.left.right = new NodeNew(2); head2.left.left.right.parent = head2.left.left; head2.left.right = new NodeNew(4); head2.left.right.parent = head2.left; head2.left.right.right = new NodeNew(5); head2.left.right.right.parent = head2.left.right; head2.right = new NodeNew(9); head2.right.parent = head2; head2.right.left = new NodeNew(8); head2.right.left.parent = head2.right; head2.right.left.left = new NodeNew(7); head2.right.left.left.parent = head2.right.left; head2.right.right = new NodeNew(10); head2.right.right.parent = head2.right; NodeNew test = head2.left.left; System.out.println(test.value + " next: " + getSuccessorNode(test).value); // 序列化与反序列化 String s = serialByPre(head); String s1 = serialByPre(head1); System.out.println(s); System.out.println(s1); Node node3 = reconByPreString(s); Node node4 = reconByPreString(s1); System.out.println(node3.value); System.out.println(node4.value); } }