面试题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;
}
}
}