面试题目——《CC150》树与图
面试题4.1:实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个结点,其两颗子树的高度差不超过1。
思路:两个方法,第一种速度较快
package cc150; public class Balance { public static void main(String[] args) { // TODO 自动生成的方法存根 } public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } public static boolean isBalance(TreeNode root){ if(checkHeight(root) == -1) return false; else return true; } public static int checkHeight(TreeNode root){ if(root == null) return 0; //检查左子树是否平衡 int leftHeight = checkHeight(root.left); if(leftHeight == -1) return -1; //检查右子树是否平衡 int rightHeight = checkHeight(root.right); if(rightHeight == -1) return -1; //检查当前结点是否平衡 int heightDiff = leftHeight - rightHeight; if(Math.abs(heightDiff) > 1) return -1; else return Math.max(leftHeight, rightHeight) + 1;//返回当前结点的高度 } // public boolean isBalance(TreeNode root) { // // write code here // int countLeft = 0; // TreeNode temp=root; // if(root == null){ // return true; // } // if(root.left==null && root.right==null){ // return true; // } // if(Math.abs(treeDepth(root.left)-treeDepth(root.right)) > 1){ // return false; // } // return isBalance(root.left) && isBalance(root.right); // // } // // public int treeDepth(TreeNode root){ // if(root == null) //如果pRoot为NULL,则深度为0,这也是递归的返回条件 // return 0; // //如果pRoot不为NULL,那么深度至少为1,所以left和right=1 // int left=1; // int right=1; // left += treeDepth(root.left); //求出左子树的深度 // right += treeDepth(root.right); //求出右子树深度 // // return (left>right?left:right); //返回深度较大的那一个 // } }
面试题4.2: 给定有向图,设计一个算法,找出两个结点之间是否存在一条路径。
思路:解法中使用了递归以及深度遍历,并没有通过栈来优化空间的占用(参考牛客网更好的解法),注意图中可能存在环和反向的问题
package cc150; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class Path { public static void main(String[] args) { // TODO 自动生成的方法存根 UndirectedGraphNode a = new UndirectedGraphNode(1); UndirectedGraphNode a1 = new UndirectedGraphNode(2); UndirectedGraphNode a2 = new UndirectedGraphNode(3); UndirectedGraphNode a3 = new UndirectedGraphNode(4); UndirectedGraphNode a11 = new UndirectedGraphNode(5); UndirectedGraphNode a12 = new UndirectedGraphNode(6); UndirectedGraphNode a13 = new UndirectedGraphNode(7); UndirectedGraphNode b = new UndirectedGraphNode(8); a1.neighbors.add(a); a.neighbors.add(a2); a.neighbors.add(a3); a.neighbors.add(a1); a11.neighbors.add(a1); a1.neighbors.add(a12); a1.neighbors.add(a13); b.neighbors.add(a11); System.out.println(checkPath(a,b)); } public static boolean checkPath(UndirectedGraphNode a, UndirectedGraphNode b) { map.clear(); boolean bool1 = checkPathCore(a,b); map.clear(); boolean bool2 = checkPathCore(b,a); return bool1 || bool2; } public static Map<UndirectedGraphNode,Boolean> map = new HashMap<UndirectedGraphNode,Boolean>(); public static boolean checkPathCore(UndirectedGraphNode a, UndirectedGraphNode b) { // write code here if(a == null || b == null) return false; if(a == b) return true; map.put(a, true); int num = a.neighbors.size(); for(int i=0;i<num;i++){ if(map.containsKey(a.neighbors.get(i)) == false && checkPathCore(a.neighbors.get(i),b))//深度遍历 return true; } return false; } public static class UndirectedGraphNode { int label = 0; UndirectedGraphNode left = null; UndirectedGraphNode right = null; ArrayList<UndirectedGraphNode> neighbors = new ArrayList<UndirectedGraphNode>(); public UndirectedGraphNode(int label) { this.label = label; } } }
package graph; public class Graph { //建立图和添加顶点 private Node vertices[]; public int count; public Graph() { vertices = new Node[6]; count = 0; } public void addNode(Node x) { if (count < 30) { vertices[count] = x; count++; } else { System.out.print("Graph full"); } } public Node[] getNodes() { return vertices; } }
package graph; class Node { private Node adjacent[]; //邻接的所有顶点,存放在一个数组中 public int adjacentCount; private String vertex; //当前顶点的值 public Question.State state; public Node(String vertex, int adjacentLength) { //adjacentLength是和该顶点邻接的顶点的数量 this.vertex = vertex; adjacentCount = 0; //每实例化一个顶点,邻接顶点的数量开始是0 adjacent = new Node[adjacentLength]; } public void addAdjacent(Node x) { //添加邻接的顶点 if (adjacentCount < 30) { //邻接的顶点的数量不要操作30 this.adjacent[adjacentCount] = x; adjacentCount++; //邻接的顶点的数量加1 } else { System.out.print("No more adjacent can be added"); } } public Node[] getAdjacent() { //返回所有的邻接顶点的数组 return adjacent; } public String getVertex() { //取得当前顶点的值 return vertex; } }
package graph; import java.util.LinkedList; public class Question { public enum State { Unvisited, Visited, Visiting; } public static void main(String a[]){ //cc150面试题4.2,给定有向图,找出两个结点之间是否存在一条路径 Graph g = createNewGraph(); Node[] n = g.getNodes(); Node start = n[3]; Node end = n[5]; System.out.println(search(g, start, end)); } public static Graph createNewGraph() { Graph g = new Graph(); Node[] temp = new Node[6]; temp[0] = new Node("a", 3); temp[1] = new Node("b", 0); temp[2] = new Node("c", 0); temp[3] = new Node("d", 1); temp[4] = new Node("e", 1); temp[5] = new Node("f", 0); temp[0].addAdjacent(temp[1]); temp[0].addAdjacent(temp[2]); temp[0].addAdjacent(temp[3]); temp[3].addAdjacent(temp[4]); temp[4].addAdjacent(temp[5]); for (int i = 0; i < 6; i++) { g.addNode(temp[i]); } return g; } public static boolean search(Graph g,Node start,Node end) { LinkedList<Node> q = new LinkedList<Node>(); for (Node u : g.getNodes()) { u.state = State.Unvisited; } start.state = State.Visiting; q.add(start); Node u; while(!q.isEmpty()) { u = q.removeFirst(); if (u != null) { for (Node v : u.getAdjacent()) { if (v.state == State.Unvisited) { if (v == end) { return true; } else { v.state = State.Visiting; q.add(v); } } } u.state = State.Visited; } } return false; } }
面试题4.3:给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一颗高度最小的二叉查找书。
思路:要创建一颗高度最小的树,必须让左右子树的结点数量越接近。即要用数组中间的值作为根节点,然后递归。
注意:题目要求的是这颗二叉树的高度 还是 创建这颗二叉树
package cc150; public class MinimalBST { public static void main(String[] args) { // TODO 自动生成的方法存根 int[] Arr = {0,1,2,3,4,5,6,7,8,9,10}; System.out.println(buildMinimalBST(Arr)); } public static int buildMinimalBST(int[] vals) { // write code here if(vals == null || vals.length <= 0) return 0; return creatMinimalBST(vals,0,vals.length-1); } // public static TreeNode creatMinimalBST(int[] vals,int start,int end) { // if(end < start) // return null; // int mid = (start+end)/2; // // TreeNode n = new TreeNode(vals[mid]); // count++; // n.left = creatMinimalBST(vals,start,mid-1); // n.right = creatMinimalBST(vals,mid+1,end); // return n; // } public static int creatMinimalBST(int[] vals,int start,int end) { if(end <= start) return 1; int mid = (start+end) >> 1; int left = 1 + creatMinimalBST(vals,start,mid-1); int right = 1 + creatMinimalBST(vals,mid+1,end); return Math.max(left, right); } public static class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } }
面试题4.4:给定一颗二叉树,设计一个算法,创建含有某一深度上所有结点的链表(比如,若一棵树的深度为D,则会创建出D个链表)。
牛客网上求的是输出单层结点
思路:方法1 层次遍历
方法2 递归遍历
使用递归遍历,去掉程序中的static用例就可以跑过,不然有用例不通过
package cc150; public class TreeLevel { public static void main(String[] args) { // TODO 自动生成的方法存根 TreeNode a1 = new TreeNode(1); TreeNode a2 = new TreeNode(2); TreeNode a3 = new TreeNode(3); TreeNode a4 = new TreeNode(4); TreeNode a5 = new TreeNode(5); TreeNode a6 = new TreeNode(6); TreeNode a7 = new TreeNode(7); a1.left = a2; a1.right = a3; a2.left = a4; a2.right = a5; a3.left = a6; a3.right = a7; TreeLevel tl = new TreeLevel(); ListNode Arr = tl.getTreeLevel(a1,2); while(Arr != null){ System.out.println(Arr.val); Arr = Arr.next; } } public ListNode start = new ListNode(-1); //链表头 public ListNode start_temp = start; //链表头的拷贝 public ListNode getTreeLevel(TreeNode root, int dep) { // write code here if (root == null || dep <= 0) //当root为null或者dep小于等于0的时候结束 return null; if (dep == 1) { //只有当dep等于1的时候加入链表 start_temp.next = new ListNode(root.val); start_temp = start_temp.next; } else { getTreeLevel(root.left, dep - 1); getTreeLevel(root.right, dep - 1); } return start.next; } public static class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public static class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } }
面试题4.5:实现一个函数,检查一颗二叉树是否为二叉查找树。
思路:方法1 中序遍历法 中序遍历的顺序是:左 中 右,把中序遍历的结果存放在一个数组之中,之后判断数组是否是升序的即可。
方法2 最小/最大法 使用递归,深度遍历并判断每一个左结点的范围是否在MIN_VALUE和父节点之间,右结点范围是否在父节点和MAX_VALUE之间
package cc150; public class CheckBST { public static void main(String[] args) { // TODO 自动生成的方法存根 TreeNode a1 = new TreeNode(4); TreeNode a2 = new TreeNode(2); TreeNode a3 = new TreeNode(6); TreeNode a4 = new TreeNode(1); TreeNode a5 = new TreeNode(3); TreeNode a6 = new TreeNode(5); TreeNode a7 = new TreeNode(7); a1.left = a2; a1.right = a3; a2.left = a4; a2.right = a5; a3.left = a6; a3.right = a7; CheckBST ch = new CheckBST(); System.out.println(ch.checkBST(a1)); } public boolean checkBST(TreeNode root) { // write code here return checkBST(root,Integer.MIN_VALUE,Integer.MAX_VALUE); } public boolean checkBST(TreeNode root,int min,int max) {//不断的缩小范围 // write code here if(root == null) return true; if(root.val < min || root.val >= max) return false; //每一个左结点的范围在MIN_VALUE和父节点之间,右结点范围在父节点和MAX_VALUE之间 if(!checkBST(root.left,min,root.val) || !checkBST(root.right,root.val,max)) return false; return true; } public static class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } }
面试题4.6:设计一个算法,找出二叉查找树中指定结点的“下一个”结点(也即中序后继)。可以假定每个结点都含有指向父节点的连接。
思路:情况1:结点2的情况,有右子树,下一个结点为右子树中最小的结点
情况2:结点1的情况,无右子树且为左孩子,返回父结点
情况3:结点3的情况,无右子树且为右孩子,返回父节点的父节点
有parent指针的解法:
package cc150; public class Successor { public static void main(String[] args) { // TODO 自动生成的方法存根 TreeNode a1 = new TreeNode(4); TreeNode a2 = new TreeNode(2); TreeNode a3 = new TreeNode(6); TreeNode a4 = new TreeNode(1); TreeNode a5 = new TreeNode(3); TreeNode a6 = new TreeNode(5); TreeNode a7 = new TreeNode(7); a1.left = a2; a1.right = a3; a2.parent = a1; a3.parent = a1; a2.left = a4; a2.right = a5; a4.parent = a2; a5.parent = a2; a3.left = a6; a3.right = a7; a6.parent = a3; a7.parent = a3; Successor su = new Successor(); System.out.println(su.findSucc(a2,2).val); System.out.println(su.findSucc(a4,1).val); System.out.println(su.findSucc(a5,3).val); } public TreeNode findSucc(TreeNode root, int p) { // write code here if(root == null) return null; if(root.right != null) //当右子树不为空的情况,找到右子树的最小值 return rightMinChild(root.right); else{ TreeNode root_copy = root; TreeNode par = root_copy.parent; while(par != null && par.left != root_copy){//向上直到位于左边,3在2的右边继续向上,直到2在4的左边 root_copy = par; par = par.parent; } return par; } } public TreeNode rightMinChild(TreeNode root){ if(root == null) return null; while(root.left != null) root = root.left; return root; } public static class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; TreeNode parent = null; public TreeNode(int val) { this.val = val; } } }
无parent指针的解法://使用中序遍历,按遍历的顺序放进ArrayList,然后通过p的值查找下一个结点
package cc150; import java.util.ArrayList; public class Successor { public static void main(String[] args) { // TODO 自动生成的方法存根 TreeNode a1 = new TreeNode(4); TreeNode a2 = new TreeNode(2); TreeNode a3 = new TreeNode(6); TreeNode a4 = new TreeNode(1); TreeNode a5 = new TreeNode(3); TreeNode a6 = new TreeNode(5); TreeNode a7 = new TreeNode(7); a1.left = a2; a1.right = a3; a2.parent = a1; a3.parent = a1; a2.left = a4; a2.right = a5; a4.parent = a2; a5.parent = a2; a3.left = a6; a3.right = a7; a6.parent = a3; a7.parent = a3; Successor su = new Successor(); System.out.println(su.findSucc(a1,2)); System.out.println(su.findSucc(a1,1)); System.out.println(su.findSucc(a1,3)); } public ArrayList<TreeNode> theList = new ArrayList<TreeNode>(); public int findSucc(TreeNode root, int p) { //使用中序遍历,按遍历的顺序放进ArrayList,然后通过p的值查找下一个结点 find(root); //其中最开始root要是树的根节点 int size = theList.size(); for(int i=0;i<size-1;i++){ //是size-1 if(theList.get(i).val == p) return theList.get(i+1).val; } return -1; } public void find(TreeNode root){ //按中序遍历的顺序把结点放进ArrayList if(root != null){ find(root.left); theList.add(root); find(root.right); } } public TreeNode rightMinChild(TreeNode root){ if(root == null) return null; while(root.left != null) root = root.left; return root; } public static class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; TreeNode parent = null; public TreeNode(int val) { this.val = val; } } }
面试题4.7:设计并实现一个算法,找出二叉树中某两个结点的第一个公共祖先。不得将额外的结点存储在另外的数据结构中。注意:这不一定是二叉查找树。
《剑指Offer》 最后的一两题
面试题4.8:你有两棵非常大的二叉树:T1,有几百万个结点;T2,有几百个结点。设计一个算法,判断T2是否为T1的子树。
如果T1有这么一个结点n,其子树与T2一模一样,则T2为T1的子树。也就是说,从结点n处把树砍掉,得到的树与T2完全相同。
package cc150; import cc150.PathSum.TreeNode; public class ContainsTree { public static void main(String[] args) { // TODO 自动生成的方法存根 TreeNode a1 = new TreeNode(4); TreeNode a2 = new TreeNode(2); TreeNode a3 = new TreeNode(6); TreeNode a4 = new TreeNode(5); TreeNode a5 = new TreeNode(7); TreeNode a6 = new TreeNode(1); TreeNode a7 = new TreeNode(3); a1.left = a2; a1.right = a3; a2.left = a4; a2.right = a5; a3.left = a6; a3.right = a7; TreeNode a66 = new TreeNode(6); TreeNode a11 = new TreeNode(1); TreeNode a33 = new TreeNode(3); a66.left = a11; a66.right = a33; ContainsTree ct = new ContainsTree(); System.out.println(ct.subTree(a1,a66)); } public boolean subTree(TreeNode r1,TreeNode r2){ //r1是大树,r2是小树,在大树中寻找和小树的根节点相等的结点 if(r1 == null) return false; if(r1.val == r2.val){ //找到和小树根节点相等的结点 if(matchTree(r1,r2)) //判断是否所有的结点都相等,相等返回true return true; } return (subTree(r1.left,r2) || subTree(r1.right,r2)); //递归所有左孩子和右孩子 } public boolean matchTree(TreeNode r1,TreeNode r2){ if(r1 == null && r2 == null) //子树中已经没有结点,子树相同 return true; if(r1 == null || r2 == null) //不同时为空说明两棵树不一样 return false; if(r1.val != r2.val) //两个结点的值不想等,说明两棵树不一样 return false; return (matchTree(r1.left,r2.left)) && (matchTree(r1.right,r2.right)); //递归左结点和右结点 } public static class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; TreeNode parent = null; public TreeNode(int val) { this.val = val; } } }
面试题4.9:给定一棵二叉树,其中每个结点都含有一个数值。设计一个算法,打印结点数值总和等于某个给定值的所有路径。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束。——《Leetcode》112 Path Sum
package cc150; import java.util.ArrayList; import java.util.Iterator; public class PathSum { public static void main(String[] args) { // TODO 自动生成的方法存根 TreeNode a1 = new TreeNode(4); TreeNode a2 = new TreeNode(2); TreeNode a3 = new TreeNode(6); TreeNode a4 = new TreeNode(5); TreeNode a5 = new TreeNode(7); TreeNode a6 = new TreeNode(1); TreeNode a7 = new TreeNode(3); a1.left = a2; a1.right = a3; a2.left = a4; a2.right = a5; a3.left = a6; a3.right = a7; PathSum sum = new PathSum(); ArrayList<ArrayList<Integer>> theArr =sum.PathSum(a1,13); Iterator ire = theArr.iterator(); while(ire.hasNext()) System.out.println(ire.next()); } public ArrayList<ArrayList<Integer>> PathSum(TreeNode root, int sum) { ArrayList<ArrayList<Integer>> arr = new ArrayList<ArrayList<Integer>>(); if(root == null) return arr; ArrayList<Integer> a = new ArrayList<Integer>(); depthTraversal(root,sum,0,arr,a); return arr; } public void depthTraversal(TreeNode root, int sum,int currentSum,ArrayList<ArrayList<Integer>> arr,ArrayList<Integer> a){ if(root == null) return; if(root != null){ currentSum += root.val; if(root.left == null && root.right == null){ //如果无子节点 if (currentSum == sum){ //如果相等 a.add(root.val); //把结点加进ArrayList arr.add(new ArrayList<Integer>(a)); //System.out.println(a); a.remove(a.size()-1); //如果相等,把ArrayList存起来,并去掉最后一个 } return; } a.add(root.val); //如果还有子结点的话,加上这个结点之后,重复左右结点 depthTraversal(root.left,sum,currentSum,arr,a); depthTraversal(root.right,sum,currentSum,arr,a); a.remove(a.size()-1); //遍历完左右子节点,去掉本身结点 } } public static class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; TreeNode parent = null; public TreeNode(int val) { this.val = val; } } }
本文只发表于博客园和tonglin0325的博客,作者:tonglin0325,转载请注明原文链接:https://www.cnblogs.com/tonglin0325/p/5877285.html