二叉树相关面试算法题(java实现)
2019-05-30 00:17
330 查看
二叉树可以说是最重要的非线性数据结构了,也是考察递归思想的重要手段。二叉树分为普通二叉树,排序二叉树,平衡二叉树,红黑树等等。
这里代码中我选择的是排序二叉树,有特殊性但又不失一般性,复杂度适中。
算法主要有(难度递增):
- 各种遍历,其中要注意的是后序非递归遍历,层序遍历(带层数,例如问第3层有几个节点)。
- 常见的统计叶子,非叶子节点数,求深度,判断左右子树相似,判断平衡等。
- 根据先序与中序建立二叉树,或者中序与后序,或者层序与中序。
- 寻找一个树中两个任意节点的公共父节点。或者在树中寻找节点和为target的路径。
暂且列出了所有可以考察的点(除红黑树及堆)
import java.util.ArrayList; import java.util.LinkedList; class Node { int key; Node left; Node right; public Node(int key) { this.key = key; left = null; right = null; } public Node() { } @Override public String toString() { // TODO Auto-generated method stub return String.valueOf(key); } //构建二叉树 public static Node Array_to_Tree(int[] a) { if (a.length == 0) return null; else { Node root = new Node(a[0]); for (int i = 1; i < a.length; i++) insert_SortTree(new Node(a[i]), root); return root; } } //插入一个节点到树中,用于构建二叉树 public static void insert_SortTree(Node temp, Node root) { if (temp.key > root.key) { if (root.right == null) root.right = temp; else insert_SortTree(temp, root.right); } else if (temp.key < root.key) { if (root.left == null) root.left = temp; else insert_SortTree(temp, root.left); } } //递归方式先序遍历 public static void visit_PreOrder(Node root) { if (root == null) return; System.out.print(root.key + " "); visit_PreOrder(root.left); visit_PreOrder(root.right); } //递归方式中序遍历 public static void visit_InOrder(Node root) { if (root == null) return; visit_InOrder(root.left); System.out.print(root.key + " "); visit_InOrder(root.right); } //递归方式后序遍历 public static void visit_PostOrder(Node root) { if (root == null) return; visit_PostOrder(root.left); visit_PostOrder(root.right); System.out.print(root.key + " "); } //非递归方式先序遍历 public static void visit_PreOrder_NotRecursive(Node root) { LinkedList<Node> linkedList = new LinkedList<>(); while (root != null || !linkedList.isEmpty()) { while (root != null) { System.out.print(root + " "); linkedList.addLast(root); root = root.left; } if (!linkedList.isEmpty()) { root = linkedList.pollLast().right; } } } //非递归方式后序遍历 public static void visit_PostOrder_NotRecursive(Node root) { if (root == null) return; LinkedList<Node> linkedList = new LinkedList<>(); LinkedList<Node> outputList = new LinkedList<>(); linkedList.addLast(root); while (!linkedList.isEmpty()) { Node node = linkedList.pollLast(); outputList.addLast(node); if (node.left != null) linkedList.addLast(node.left); if (node.right != null) linkedList.addLast(node.right); } while (!outputList.isEmpty()) System.out.print(outputList.pollLast() + " "); } //非递归方式中序遍历 public static void visit_InOrder_NotRecursive(Node root) { LinkedList<Node> linkedList = new LinkedList<>(); while (root != null || !linkedList.isEmpty()) { while (root != null) { linkedList.addLast(root); root = root.left; } if (!linkedList.isEmpty()) { Node temp = linkedList.pollLast(); System.out.print(temp + " "); root = temp.right; } } } //层序遍历 public static void visit_Level(Node root) { if (root == null) return; else { LinkedList<Node> linkedList = new LinkedList<>(); linkedList.addLast(root); int level = 0, curLevelNode = 1, nextLevelNode = 0; while (!linkedList.isEmpty()) { Node temp = linkedList.pollFirst(); System.out.print(temp.key + "(" + level + ")" + " "); curLevelNode--; if (temp.left != null) { linkedList.addLast(temp.left); nextLevelNode++; } if (temp.right != null) { linkedList.addLast(temp.right); nextLevelNode++; } if (curLevelNode == 0) { level++; curLevelNode = nextLevelNode; nextLevelNode = 0; } } } System.out.println(); } //统计叶节点个数 public static int count_LeafNode(Node root) { if (root == null) return 0; else if (root.left == null && root.right == null) return 1; else return count_LeafNode(root.left) + count_LeafNode(root.right); } //统计非叶节点个数 public static int count_NotLeafNode(Node root) { if (root == null || (root.left == null && root.right == null)) return 0; else return count_NotLeafNode(root.left) + count_NotLeafNode(root.right) + 1; } //统计所有节点个数 public static int count_AllNode(Node root) { if (root == null) return 0; else return count_AllNode(root.left) + count_AllNode(root.right) + 1; } // 计算第k层节点数 public static int count_LevelNode(Node root, int k) { if (root == null) return 0; int depth = 0; int current_LevelNode = 1; int next_LevelNode = 0; LinkedList<Node> linkedList = new LinkedList<>(); linkedList.addLast(root); while (!linkedList.isEmpty()) { Node temp = linkedList.pollFirst(); current_LevelNode--; if (temp.left != null) { linkedList.addLast(temp.left); next_LevelNode++; } if (temp.right != null) { linkedList.addLast(temp.right); next_LevelNode++; } if (current_LevelNode == 0) { current_LevelNode = next_LevelNode; next_LevelNode = 0; depth++; } if (depth == k) { return current_LevelNode; } } return 1; } // 计算各层节点数 public static int[] count_LevelNode(Node root) { int[] count = new int[100]; if (root == null) return null; LinkedList<Node> linkedList = new LinkedList<>(); int start = 0, end = 0, level = 0, last = 1; linkedList.addLast(root); end++; while (!linkedList.isEmpty()) { Node temp = linkedList.pollFirst(); start++; count[level]++; if (temp.left != null) { linkedList.addLast(temp.left); end++; } if (temp.right != null) { linkedList.addLast(temp.right); end++; } if (start == last) { last = end; level++; } } System.out.print("各层节点数为:"); for (int i = 0; i < level; i++) { int j = count[i]; System.out.print(j + " "); } System.out.println(); return count; } private static int max(int a, int b) { if (a > b) return a; else return b; } private static int min(int a, int b) { if (a < b) return a; else return b; } //计算最大深度 public static int depth(Node root) { if (root == null) return 0; else return max(depth(root.left), depth(root.right)) + 1; } //树的最大枝长 public static int MaxLeaf(Node root) { if (root == null) return 0; else return max(MaxLeaf(root.left), MaxLeaf(root.right)) + 1; } //树的最小枝长 public static int MinLeaf(Node root) { if (root == null) return 0; else return min(MinLeaf(root.left), MinLeaf(root.right)) + 1; } //获取节点深度,用于判断该二叉树是否是平衡二叉树的函数 //注意及时return 剪枝手法 private static int getDepth(Node root){ if(root == null) return 0; int left = getDepth(root.left); if(left == -1) return -1; int right = getDepth(root.right); if(right == -1) return -1; return Math.abs(left - right) >1 ? -1:1+Math.max(left, right); } //判断该二叉树是否是平衡二叉树 public static boolean isBalanced(Node root) { return getDepth(root) != -1; } //复制一颗树 public static Node CopyTree(Node root) { if (root == null) return null; else { Node temp = new Node(root.key); temp.left = CopyTree(root.left); temp.right = CopyTree(root.right); return temp; } } //交换左右子树 public static void Swap_Tree(Node root) { if (root == null) return; else { Node temp = root.left; root.left = root.right; root.right = temp; Swap_Tree(root.left); Swap_Tree(root.right); } } // 判断两颗树是否相似 public static boolean Tree_Like(Node root1, Node root2) { if (root1 == null && root2 == null) return true; else if (root1 != null && root2 != null && root1.key == root2.key) return Tree_Like(root1.left, root2.left) && Tree_Like(root1.right, root2.right); return false; } /** * * @param a * 先序 * @param b * 中序 * @param start_a * 起始下标 * @param start_b * 起始下标 * @param len * 长度 * @return */ //根据先序序列和中序序列建立二叉树 public static Node createTreeBy_PreOrder_and_InOrder(int[] a, int[] b, int start_a, int start_b, int len) { if (len <= 0) return null; Node root = new Node(a[start_a]); int k = 0; for (int i = 0; i < len; i++) { if (b[i] == root.key) { k = i; break; } } root.left = createTreeBy_PreOrder_and_InOrder(a, b, start_a + 1, start_b, k); root.right = createTreeBy_PreOrder_and_InOrder(a, b, start_a + k + 1, start_b + k + 1, len - k - 1); return root; } //根据后序序列和中序序列建立二叉树 public static Node createTreeBy_InOrder_and_PostOrder(int[] a, int[] b, int start_a, int start_b, int len) { if (len <= 0) return null; Node root = new Node(b[start_b + len - 1]); int k = 0; for (int i = 0; i < len; i++) { if (a[i] == root.key) { k = i; break; } } root.left = createTreeBy_InOrder_and_PostOrder(a, b, start_a, start_b, k); root.right = createTreeBy_InOrder_and_PostOrder(a, b, start_a + k + 1, start_b + k, len - k - 1); return root; } //根据层序序列和中序序列建立二叉树 public static Node createTreeBy_InOrder_and_LevelOrder(int[] levelOrder, int[] inOrder, int start_inOrder, int end_inOrder) { if (start_inOrder >= end_inOrder) return null; Node root = null; boolean stop = false; int i = 0, j = start_inOrder; for (i = 0; i < levelOrder.length && !stop; i++) { for (j = start_inOrder; j < end_inOrder && !stop; j++) { if (inOrder[j] == levelOrder[i]) { root = new Node(inOrder[j]); stop = true; } } } root.left = createTreeBy_InOrder_and_LevelOrder(levelOrder, inOrder, start_inOrder, j - 1); root.right = createTreeBy_InOrder_and_LevelOrder(levelOrder, inOrder, j, end_inOrder); return root; } //只访问叶子节点 public static void visit_LeafNode(Node root) { if (root == null) return; if (root.right == null && root.left == null) System.out.print(root + " "); visit_LeafNode(root.left); visit_LeafNode(root.right); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + key; result = prime * result + ((left == null) ? 0 : left.hashCode()); result = prime * result + ((right == null) ? 0 : right.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Node other = (Node) obj; if (key != other.key) return false; if (left == null) { if (other.left != null) return false; } else if (!left.equals(other.left)) return false; if (right == null) { if (other.right != null) return false; } else if (!right.equals(other.right)) return false; return true; } //根据值获取一个节点的引用 public static Node findNode(Node root, int key) { if(root == null) return null; if(root.key == key) return root; Node res1 = findNode(root.left, key); if(res1 != null) return res1; Node res2 = findNode(root.right, key); if(res2 != null) return res2; return null; } /* * 寻找一个节点到根节点的路径 * 为了偷懒使用了全局变量 */ public static ArrayList<Node> arrayList_findPathNodeToRoot = new ArrayList<>(); private static ArrayList<Node> temp_findPathNodeToRoot = new ArrayList<>(); public static void findPathNodeToRoot(Node root, Node node) { if(root == null) return; temp_findPathNodeToRoot.add(root); if(root == node) { //注意这里要复制一份temp保存起来,防止在回溯过程中其中的引用被改变 arrayList_findPathNodeToRoot = new ArrayList<>(temp_findPathNodeToRoot); return;} findPathNodeToRoot(root.left, node); findPathNodeToRoot(root.right, node); temp_findPathNodeToRoot.remove(temp_findPathNodeToRoot.size()-1); } /* * 寻找一个树中两个任意节点的公共父节点 * 思想是用刚才的"寻找一个节点到根节点的路径" * 问题转化为求两条链表的第一个公共交点(这里是顺序表,但是差不多) */ public static Node findParent(Node root, Node node1, Node node2) { //这里因为list是全局static变量,使用前要清空一下 arrayList_findPathNodeToRoot.clear(); temp_findPathNodeToRoot.clear(); findPathNodeToRoot(root, node1); ArrayList<Node> list1 = new ArrayList<>(arrayList_findPathNodeToRoot); arrayList_findPathNodeToRoot.clear(); temp_findPathNodeToRoot.clear(); findPathNodeToRoot(root, node2); ArrayList<Node> list2 = new ArrayList<>(arrayList_findPathNodeToRoot); //下面开始 for(int i = 0; i < list1.size() && i < list2.size(); i++) { if(i == list1.size()-1 || i == list2.size()-1) return list1.get(i); if(list1.get(i+1) != list2.get(i+1)) return list1.get(i); } return list1.get(list1.size()-1); } /* * 寻找节点和为target的路径 * 为了偷懒使用了全局变量 */ public static ArrayList<ArrayList<Node>> arrayList_findSumPath = new ArrayList<>(); public static ArrayList<Node> temp_findSumPath = new ArrayList<>(); public static void findSumPath(Node root, int target) { if (root == null) return; temp_findSumPath.add(root); target = target - root.key; if(target == 0) {arrayList_findSumPath.add(new ArrayList<>(temp_findSumPath));} findSumPath(root.left, target); findSumPath(root.right, target); temp_findSumPath.remove(temp_findSumPath.size()-1); } } public class Main { public static void main(String[] args) { int[] a = { 5, 3, 1, 7, 2, 4, 6, 10 }; Node root = Node.Array_to_Tree(a); System.out.print("中序遍历结果为:"); Node.visit_InOrder_NotRecursive(root); System.out.println(); System.out.print("后序遍历结果为:"); Node.visit_PostOrder_NotRecursive(root); System.out.println(); System.out.print("层次遍历结果为:"); Node.visit_Level(root); System.out.print("从左到右遍历叶子节点结果为:"); Node.visit_LeafNode(root); System.out.println(); System.out.print("所有节点总数为:" + Node.count_AllNode(root)); System.out.println(" 叶子节点总数为:" + Node.count_LeafNode(root)); System.out.print("非叶子节点总数为:" + Node.count_NotLeafNode(root)); System.out.println(" 最大枝长为:" + Node.MaxLeaf(root)); System.out.print("最小枝长为:" + Node.MinLeaf(root)); System.out.println(" 数的高度为:" + Node.depth(root)); Node root2 = Node.CopyTree(root); System.out.print("交换左右子树后层次遍历结果为:"); Node.Swap_Tree(root2); Node.visit_Level(root2); System.out.print("两个树是否相等:" + Node.Tree_Like(root, root2)); System.out.println(" 第k层节点数为:" + Node.count_LevelNode(root, 2)); Node.count_LevelNode(root); Node r1 = Node.createTreeBy_PreOrder_and_InOrder(new int[] { 2, 3, 4, 5, 6 }, new int[] { 4, 3, 5, 2, 6 }, 0, 0, 5); System.out.print("由前序和中序确定的二叉树的后序遍历为 : "); Node.visit_PostOrder(r1); System.out.println(); Node r2 = Node.createTreeBy_InOrder_and_PostOrder(new int[] { 4, 3, 5, 2, 6 }, new int[] { 4, 5, 3, 6, 2 }, 0, 0, 5); System.out.print("由中序和后序确定的二叉树的前序遍历为 : "); Node.visit_PreOrder(r2); System.out.println(); Node r3 = Node.createTreeBy_InOrder_and_LevelOrder(new int[] { 1, 2, 3, 4, 5 }, new int[] { 2, 4, 1, 5, 3 }, 0, 5); System.out.print("由层序和中序确定的二叉树的后序遍历为 : "); Node.visit_PostOrder(r3); System.out.println(); System.out.print("路径和为12的路径有:"); Node.findSumPath(root, 12); for (ArrayList<Node> temp : Node.arrayList_findSumPath) { for(Node i : temp) System.out.print(i.key+" "); System.out.print(" # "); } System.out.println(); System.out.println("获取值为1的节点:"+Node.findNode(root, 1)); System.out.print("从根节点到节点2的路径为:"); Node.findPathNodeToRoot(root, Node.findNode(root, 2)); for (Node i : Node.arrayList_findPathNodeToRoot) { System.out.print(i+" "); }System.out.println(); System.out.print("节点2和节点6的公共父节点为:"+Node.findParent(root, Node.findNode(root, 2), Node.findNode(root, 10))); } }
相关文章推荐
- 链表相关面试算法题 java实现
- 【LeetCode-面试算法经典-Java实现】【104-Maximum Depth of Binary Tree(二叉树的最大深度)】
- 【LeetCode-面试算法经典-Java实现】【111-Minimum Depth of Binary Tree(二叉树的最小深度)】
- 【LeetCode-面试算法经典-Java实现】【145-Binary Tree Postorder Traversal(二叉树非递归后序遍历)】
- 【LeetCode-面试算法经典-Java实现】【199-Binary Tree Right Side View(从右边看二叉树)】
- 【LeetCode-面试算法经典-Java实现】【226-Invert Binary Tree(反转二叉树)】
- 【LeetCode-面试算法经典-Java实现】【114-Flatten Binary Tree to Linked List(二叉树转单链表)】
- 【LeetCode-面试算法经典-Java实现】【144-Binary Tree Preorder Traversal(二叉树非递归前序遍历)】
- 【LeetCode-面试算法经典-Java实现】【107-Binary Tree Level Order Traversal II(二叉树层序遍历II)】
- 【LeetCode-面试算法经典-Java实现】【107-Binary Tree Level Order Traversal II(二叉树层序遍历II)】
- 【LeetCode-面试算法经典-Java实现】【116-Populating Next Right Pointers in Each Node(二叉树链接右指针)】
- 【LeetCode-面试算法经典-Java实现】【114-Flatten Binary Tree to Linked List(二叉树转单链表)】
- 【LeetCode-面试算法经典-Java实现】【117-Populating Next Right Pointers in Each Node(二叉树链接右指针II)】
- 【LeetCode-面试算法经典-Java实现】【144-Binary Tree Preorder Traversal(二叉树非递归前序遍历)】
- 笔试面试算法经典--二叉树的镜像-递归与非递归实现(Java)
- 【LeetCode-面试算法经典-Java实现】【111-Minimum Depth of Binary Tree(二叉树的最小深度)】
- 【LeetCode-面试算法经典-Java实现】【199-Binary Tree Right Side View(从右边看二叉树)】
- 【LeetCode-面试算法经典-Java实现】【145-Binary Tree Postorder Traversal(二叉树非递归后序遍历)】
- 【LeetCode-面试算法经典-Java实现】【105-Construct Binary Tree from Preorder and Inorder Traversal(构造二叉树)】
- 【LeetCode-面试算法经典-Java实现】【104-Maximum Depth of Binary Tree(二叉树的最大深度)】