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

 

 

 

 

posted @ 2016-09-16 21:33  tonglin0325  阅读(311)  评论(0编辑  收藏  举报