面试题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 map = new HashMap();
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
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 neighbors = new ArrayList();
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 q = new LinkedList();
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 theList = new ArrayList();
public int findSucc(TreeNode root, int p) {//使用中序遍历,按遍历的顺序放进ArrayList,然后通过p的值查找下一个结点
find(root);//其中最开始root要是树的根节点
int size = theList.size();
for(int i=0;i
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> theArr =sum.PathSum(a1,13);
Iterator ire = theArr.iterator();
while(ire.hasNext())
System.out.println(ire.next());
}
public ArrayList> PathSum(TreeNode root, int sum) {
ArrayList> arr = new ArrayList>();
if(root == null)
return arr;
ArrayList a = new ArrayList();
depthTraversal(root,sum,0,arr,a);
return arr;
}
public void depthTraversal(TreeNode root, int sum,int currentSum,ArrayList> arr,ArrayList 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(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;
}
}
}