cc150 java_面试题目——《CC150》树与图

f093a25340781aa617fa38cd440e10ee.png

b0ae894a4c762bda754d80e69cf565ed.png

eb2519c4b034cdc16b95ac25e3f530a9.png

面试题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:设计一个算法,找出二叉查找树中指定结点的“下一个”结点(也即中序后继)。可以假定每个结点都含有指向父节点的连接。

598dfd9ebbb4a7ace83568e6d7e3226f.png

思路:情况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;

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值