剑指Offer面试题(第三十六天)面试题63、64、65、66、67、68(1)、68(2)、68(3)

* 面试题63:股票的最大利润


     * 题目:假设把某股票的价格按照时间先后顺序存储在数组中,
     * 请问买卖该股票一次可能获得的最大利润是多少?
     * 例如:一只股票在某些时间节点的价格为{9,11,8,5,7,12,16,14}.
     * 如果能在价格为5的时候买入并在价格为16时卖出,则能收获的最大利润11
     * 
     * 
     * 思路:设定一个最小值和最大利润(差值),遍历数组,发现有比最小值小的则更新最小值
     * 若差值有比记录的最大利润大的 则更新最大利润
     * 时间复杂度O(n)

package Test;

public class No63MaxDiff {

	/*
	 * 面试题63:股票的最大利润
	 * 题目:假设把某股票的价格按照时间先后顺序存储在数组中,
	 * 请问买卖该股票一次可能获得的最大利润是多少?
	 * 例如:一只股票在某些时间节点的价格为{9,11,8,5,7,12,16,14}.
	 * 如果能在价格为5的时候买入并在价格为16时卖出,则能收获的最大利润11
	 * 
	 * 
	 * 思路:设定一个最小值和最大利润(差值),遍历数组,发现有比最小值小的则更新最小值
	 * 若差值有比记录的最大利润大的 则更新最大利润
	 * 时间复杂度O(n)
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		No63MaxDiff m = new No63MaxDiff();
		int[] values = {9,11,8,5,7,12,16,14};
		System.out.println("股票的最大利润:"+m.MaxDiff(values));
	}

	private int MaxDiff(int[] values) {
		// TODO Auto-generated method stub
		if(values == null || values.length == 0)
			return -1;
		
		int min = values[0];
		int maxDiff = values[1] - min;
		
		for(int i = 2; i < values.length;i++) {
			if(values[i-1] < min)
				min = values[i-1];
			int currentDiff = values[i] - min;
			if(currentDiff > maxDiff)
				maxDiff = currentDiff;
		}
		
		return maxDiff;
	}

}

 * 面试题64:求1+2+3+...+n


     * 题目:求1+2+3+...+你,要求不能使用乘除法、for、while、if、
     * else、switch、case等关键字及条件判断语句(A?B:C)
     * 
     * 思路:与n有关,题目有限制,首先想到使用数组解决,将数累加放到构造函数中。
     * 但是数组不用for无法完成直接初始化,所以此方法不可行
     * 因而使用&&进行截断
     * &&:在第一个条件不成立时,不在进行第二个条件的判断
 

package Test;

public class No64Sum {

	/*
	 * 面试题64:求1+2+3+...+n
	 * 题目:求1+2+3+...+你,要求不能使用乘除法、for、while、if、
	 * else、switch、case等关键字及条件判断语句(A?B:C)
	 * 
	 * 思路:与n有关,题目有限制,首先想到使用数组解决,将数累加放到构造函数中。
	 * 但是数组不用for无法完成直接初始化,所以此方法不可行
	 * 因而使用&&进行截断
	 * &&:在第一个条件不成立时,不在进行第二个条件的判断
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No64Sum s = new No64Sum();
		int n = 10;
		System.out.println("从1-10的累加和为:"+s.Sum(n));
				
	}

	//灵活运用递归的方式实现
	private int Sum(int n) {
		// TODO Auto-generated method stub
		int sum = 0;
		//&&:在前一个条件下不成立时,后一个条件将不再进行判断
		//也就是判断: n>0  如该条件不成立 则后面的sum+=则不再进行累加 
		//这样就保证一直,累加到1为止
		boolean b = (n > 0)&&((sum += Sum(n-1) + n)>0);
		return sum;
	}

}

     * 面试题65:不用加减乘除做加法


     * 题目:写一个函数,求两个整数之和,
     * 要求在函数体内不得使用“+”、“-”,“*”,“/”四则运算符号
     * 
     * 思路:对两个字使用二进制的位运算
     * 1>  首先对每一位进行相加(不考虑进位),使用异或的方式对每一位进行相加  相同为0,不同为1
     * 2>  然后再对每一位求进位(先与运算,然后再进行向左移动一位)求得进位的置
     * 3>  最后再将前两步的操作进行相加------>也就是重复刚才1>,2>过程
     * 重复执行该操作 直到无进位为止

package Test;

public class No65Add {

	/*
	 * 面试题65:不用加减乘除做加法
	 * 题目:写一个函数,求两个整数之和,
	 * 要求在函数体内不得使用“+”、“-”,“*”,“/”四则运算符号
	 * 
	 * 思路:对两个字使用二进制的位运算
	 * 1>  首先对每一位进行相加(不考虑进位),使用异或的方式对每一位进行相加  相同为0,不同为1
	 * 2>  然后再对每一位求进位(先与运算,然后再进行向左移动一位)求得进位的置
	 * 3>  最后再将前两步的操作进行相加------>也就是重复刚才1>,2>过程
	 * 重复执行该操作 直到无进位为止
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No65Add p = new No65Add();
		int m = 5;
		int n = 17;
		System.out.println("两个整数之和为:"+p.Add(m,n));
	}

	private int Add(int m, int n) {
		// TODO Auto-generated method stub
		if(n == 0)
			return m;
		
		while(n != 0) {
			//使用异或 对每一位求和
			int temp = m ^ n;
			//使用与操作+左移操作  对每一位求进位
			n = (m & n) << 1;
			m = temp;
		}
		
		return m;
	}

}

 * 面试题66:构建乘积数组


     * 题目:给定一个数组A[0,1,2,...,n-1],请构建一个数组B[0,1,2,...,n],
     * 其中B中的元素B[i] =A[0]*A[1]*...*A[i-1]*A[i+1]*...A[n-1],不能使用除法
     * 
     * 思路:
     *  B[0]   =   1  * A[1] * A[2] * ... * A[n-2] * A[n-1]
     *  B[1]   = A[0] *   1  * A[2] * ... * A[n-2] * A[n-1]
     *  B[2]   = A[0] * A[1] *   1  * ... * A[n-2] * A[n-1]
     *  ......
     *  B[n-2] = A[0] * A[1] * A[2] * ... *    1   * A[n-1]
     *  B[n-1] = A[0] * A[1] * A[2] * ... * A[n-2] *   1
     * 
     * 然后对其进行上三角以及下三角区域相乘之积  即为所求
     * 上三角 由上到下计算
     * 下三角  由下到上计算

package Test;

public class No66Multiply {

	/*
	 * 面试题66:构建乘积数组
	 * 题目:给定一个数组A[0,1,2,...,n-1],请构建一个数组B[0,1,2,...,n],
	 * 其中B中的元素B[i] =A[0]*A[1]*...*A[i-1]*A[i+1]*...A[n-1],不能使用除法
	 * 
	 * 思路:
	 *  B[0]   =   1  * A[1] * A[2] * ... * A[n-2] * A[n-1]
	 *  B[1]   = A[0] *   1  * A[2] * ... * A[n-2] * A[n-1]
	 *  B[2]   = A[0] * A[1] *   1  * ... * A[n-2] * A[n-1]
	 *  ......
	 *  B[n-2] = A[0] * A[1] * A[2] * ... *    1   * A[n-1]
	 *  B[n-1] = A[0] * A[1] * A[2] * ... * A[n-2] *   1
	 * 
	 * 然后对其进行上三角以及下三角区域相乘之积  即为所求
	 * 上三角 由上到下计算
	 * 下三角  由下到上计算
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No66Multiply m = new No66Multiply();
		int[] A = {1,2,3,4,3,2,1};
		int[] B = m.Multiply(A);
		for(int i = 0;i < A.length;i++) {
			System.out.println(B[i]+",");
		}
		
	}

	private int[] Multiply(int[] A) {
		// TODO Auto-generated method stub
		if(A == null)
			return null;
		if(A.length == 0)
			return new int[0];
		
		int[] result = new int[A.length];
		result[0] = 1;
		//下三角C[i] = A[0]*A[1]*...*A[i-1]   
		//由上到下顺序计算  C[i] = C[i-1]*A[i-1]
		//其中result[i]为C[i] 
		for(int i = 1;i < A.length;i++)
			result[i] = result[i-1]*A[i-1];
		
		//上三角D[i] = A[i+1]*A[i+2]*...*A[n-1] 
		//由下到上顺序计算 D[i] = D[i+1]*A[i+1]
		int temp = 1;
		//其中  从下到上
		//temp = A[A.length-1],
		//temp = A[A.length-1]*A[A.length-2]
		//,...,
		//temp = A[A.length-1]*A{A.length-2}*...*A[0]
		//result[i] = result[i] * temp  就是B{i]的结果		for(int i = A.length - 2; i >= 0;i--) {
		for(int i = A.length - 2;i >= 0;i++) {
			temp *= A[i + 1];
			result[i] *= temp;
		}
		
		return result;
	}

}

 * 面试题67:把字符串转换成整数


     * 题目:将一个字符串转换成一个整数,要求不能使用字符串转换为证书的库函数
     * 数字为0或者字符串不是一个合法的数值,则返回0
     * 
     * 思路:
     * 需要考虑非法输入和边界值条件
     * 合法输入:
     * 1> 数字       
     * 2>+/-数字
     * 
     * 非法输入:
     * 1>null      
     * 2>字符串为空
     * 3>字符串只有一个符号+/-
     * 4>字符串中由非数字字符
     * 5>字符串超过int所能表示的范围  溢出

package Test;

public class No67StrToInt {

	/*
	 * 面试题67:把字符串转换成整数
	 * 题目:将一个字符串转换成一个整数,要求不能使用字符串转换为证书的库函数
	 * 数字为0或者字符串不是一个合法的数值,则返回0
	 * 
	 * 思路:
	 * 需要考虑非法输入和边界值条件
	 * 合法输入:
	 * 1> 数字       
	 * 2>+/-数字
	 * 
	 * 非法输入:
	 * 1>null      
	 * 2>字符串为空
	 * 3>字符串只有一个符号+/-
	 * 4>字符串中由非数字字符
	 * 5>字符串超过int所能表示的范围  溢出
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No67StrToInt p = new No67StrToInt();
		String s = "+889";
		System.out.println("字符串转为整数:"+p.StrToInt(s));
	}

	private int StrToInt(String s) {
		// TODO Auto-generated method stub
		if(s == null || s == "")
			return 0;
		int index = 0;   //下标
		long result = 0;
		boolean isPositive = true;   //记录正负号标志位
		
		//若有符号
		if(s.charAt(0) == '+' || s.charAt(0) == '-') {
			++index;
			//若只有正负号  则返回0
			if(index == s.length())
				return 0;
			if(s.charAt(0) == '-')
				isPositive = false;
		}
		//因为是整数所以不必考虑小数点      123 中s.charAt(0) = 1;s.charAt(2) = 3;
		for(int i = index;i < s.length();i++) {
			if(s.charAt(i) < '0' || s.charAt(0) > '9')
				return 0;
			result *= 10;
			result += (s.charAt(i) - '0')*(isPositive?1:-1);
			if(result > Integer.MAX_VALUE || result < Integer.MIN_VALUE)
				return 0;
		}
		return (int)result;
	}

}

* 面试题68:最低公共祖先


     * 给出树中的两个节点,求这两个节点的最低公共祖先


     * 题目一:树是二叉搜索树

 
     * 思路:时间复杂度O(logn)     空间复杂度O(1)
     * 从树的根节点开始遍历,若根节点的值大于其中一个节点,小于另外一个节点,则根节点就是最低公共节点。
     * 若根结点的值小于两个节点的值,则递归求其右子树
     * 若根节点的值大于两个节点的值,则递归求其左子树
     * 若根节点的值等于其中的某一个节点,那么说明这两个节点在一条路径上,所以最低公共祖先则是根节点的父节点
 

package Test;

import Test.No55First_TreeDepth.BinaryTreeNode;

public class No68First_GetLowestCommonAncestorBinaryTree {

	/*
	 * 面试题68:最低公共祖先
	 * 给出树中的两个节点,求这两个节点的最低公共祖先
	 * 题目一:树是二叉搜索树
	 * 
	 * 
	 * 思路:时间复杂度O(logn)     空间复杂度O(1)
	 * 从树的根节点开始遍历,若根节点的值大于其中一个节点,小于另外一个节点,则根节点就是最低公共节点。
	 * 若根结点的值小于两个节点的值,则递归求其右子树
	 * 若根节点的值大于两个节点的值,则递归求其左子树
	 * 若根节点的值等于其中的某一个节点,那么说明这两个节点在一条路径上,所以最低公共祖先则是根节点的父节点
	 * 
	 * */
	static class BinaryTreeNode{
		int val;
		BinaryTreeNode left;
		BinaryTreeNode right;
		
		BinaryTreeNode(int val){
			this.val = val;
		}
		
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		No68First_GetLowestCommonAncestorBinaryTree p =  new No68First_GetLowestCommonAncestorBinaryTree();
		BinaryTreeNode root1 = new BinaryTreeNode(5);
		BinaryTreeNode one = new BinaryTreeNode(3);
		BinaryTreeNode two = new BinaryTreeNode(7);
		BinaryTreeNode three = new BinaryTreeNode(2);
		BinaryTreeNode four = new BinaryTreeNode(4);
		BinaryTreeNode five = new BinaryTreeNode(6);
		BinaryTreeNode six = new BinaryTreeNode(8);
		BinaryTreeNode seven = new BinaryTreeNode(1);
		
		root1.left = one;
		root1.right = two;
		one.left = three;
		one.right = four;
		two.left = five;
		two.right = six;
		three.left = seven;
		three.right = null;
		four.left = null;
		four.right = null;
		five.left = null;
		five.right = null;
		six.left = null;
		six.right = null;
		seven.right = null;
		seven.left = null;
		
		System.out.println("两个节点的最低公共祖先:"+p.GetLowestCommonAncestor(root1,root1,seven,four).val);
		
		
	}
	
	private BinaryTreeNode GetLowestCommonAncestor(BinaryTreeNode rootParent,BinaryTreeNode root1, BinaryTreeNode node1, BinaryTreeNode node2) {
		// TODO Auto-generated method stub
		if(root1 == null || node1 == null || node2 == null)
			return null;
		//若根节点在两个节点之间  则返回根节点
		if((root1.val - node1.val)*(root1.val - node2.val) < 0)
			return root1;
		else if((root1.val - node1.val)*(root1.val - node2.val) > 0) {
			//判断是左右子树中的哪一个
			BinaryTreeNode newRoot = ((root1.val > node1.val)&&(root1.val > node2.val))?root1.left:root1.right;
			return GetLowestCommonAncestor(root1,newRoot,node1,node2);
		}
		else
			return rootParent;
		
	}
	
	
	

}

 * 题目二:树是普通树,但有指向父节点的指针


     * 思路:两个节点若是在两条路径上,类似于两个链表的第一个公共节点。
     * 由于每个节点的深度最多为logn,所以时间复杂度O(logn),空间复杂度O(1)

package Test;

public class No68Second_FetLowestCommonAncestorTreeParent {

	/*
	 * 面试题68:最低公共祖先
	 * 给出树中的两个节点,求这两个节点的最低公共祖先
	 * 题目二:树是普通树,但有指向父节点的指针
	 * 
	 * 
	 * 思路:两个节点若是在两条路径上,类似于两个链表的第一个公共节点。
	 * 由于每个节点的深度最多为logn,所以时间复杂度O(logn),空间复杂度O(1)
	 * 
	 * */
	
	static class TreeNode{
		int val;
		TreeNode one;
		TreeNode two;
		TreeNode three;
		TreeNode parent;
		
		TreeNode(int val){
			this.val = val;
		}
		
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		No68Second_FetLowestCommonAncestorTreeParent p =  new No68Second_FetLowestCommonAncestorTreeParent();

		TreeNode root1 = new TreeNode(5);
		TreeNode one = new TreeNode(3);
		TreeNode two = new TreeNode(7);
		TreeNode three = new TreeNode(2);
		TreeNode four = new TreeNode(4);
		TreeNode five = new TreeNode(6);
		TreeNode six = new TreeNode(8);
		
		root1.one = one;
		root1.two = two;
		root1.three = three;
		one.one = null;
		one.two = null;
		one.three = null;
		two.one = five;
		two.two = six;
		two.three = null;
		three.one = null;
		three.two = null;
		three.three = null;
		four.one = six;
		four.two = null;
		four.three = null;
		five.one = null;
		five.two = null;
		five.three = null;
		six.one = null;
		six.two = null;
		six.three = null;
		
		one.parent = root1;
		two.parent = root1;
		three.parent = root1;
		four.parent = one;
		five.parent = one;
		six.parent = four;
		
		System.out.println("两个节点的最低公共祖先:"+p.GetLowestCommonAncestor(root1,six,two).val);

	}
	private TreeNode GetLowestCommonAncestor(TreeNode root1, TreeNode node1, TreeNode node2) {
		// TODO Auto-generated method stub
		if(root1 == null || node1 == null || node2 == null)
			return null;
		
		int depth1 = findTheDepthOfTheNode(root1,node1,node2);
		if(depth1 == -1)   //两个节点在一条路径上   则返回后一个的父节点即node2.parent
			return node2.parent;
		
		int depth2 = findTheDepthOfTheNode(root1,node2,node1);
		if(depth2 == -1)//两个节点在一条路径上   则返回后一个的父节点即node1.parent
			return node1.parent;
		
		//p 指向较深的节点
		//q 指向较浅的节点
		TreeNode p = (depth1 > depth2)?node1:node2;
		TreeNode q = (depth1 > depth2)?node2:node1;
		//二者深度的差值  的绝对值
		int depth = Math.abs(depth1-depth2); 

		//先将长的那一个率先移动差值个节点
		while(depth > 0) {
			p = p.parent;
			depth -- ;
		}
		//然后两个一起移动   直到找到最小公共祖先
		while(p != q) {
			p = p.parent;
			q = q.parent;
		}
		return p;
	}
	
	//求node1,node2深度,若node1和node2在一条路径上,则返回-1;否则返回node1的深度
	private int findTheDepthOfTheNode(TreeNode root1, TreeNode node1, TreeNode node2) {
		// TODO Auto-generated method stub
		int depth = 0;
		//从叶子节点向上遍历
		while(node1.parent != null) {
			node1 = node1.parent;
			depth++;
			//node1 == node2表示node1和node2在一条路径上
			//且 node2是node1的父节点 
			if(node1 == node2)
				return -1;
		}
		
		return depth;
	}

}


     * 题目三:树是普通树,没有指向父节点的指针


     * 思路:用栈的方式来实现类似于指向父节点指针的功能,
     * 获取node节点的路径时间复杂度为O(n),所以总的时间复杂度O(n),空间复杂度O(logn)
 

package Test;

import java.util.Stack;

public class No68Third_FetLowestCommonAncestorTree {

	/*
	 * 面试题68:最低公共祖先  *********难!!!!!
	 * 给出树中的两个节点,求这两个节点的最低公共祖先
	 * 题目三:树是普通树,没有指向父节点的指针
	 * 
	 * 思路:用栈的方式来实现类似于指向父节点指针的功能,
	 * 获取node节点的路径时间复杂度为O(n),所以总的时间复杂度O(n),空间复杂度O(logn)
	 * 
	 * */
	
	static class TreeNode{
		int val;
		TreeNode one;
		TreeNode two;
		TreeNode three;
		
		TreeNode(int val){
			this.val = val;
		}
		
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No68Third_FetLowestCommonAncestorTree p =  new No68Third_FetLowestCommonAncestorTree();

		TreeNode root1 = new TreeNode(5);
		TreeNode one = new TreeNode(3);
		TreeNode two = new TreeNode(7);
		TreeNode three = new TreeNode(2);
		TreeNode four = new TreeNode(4);
		TreeNode five = new TreeNode(6);
		TreeNode six = new TreeNode(8);
		
		root1.one = one;
		root1.two = two;
		root1.three = three;
		one.one = five;
		one.two = six;
		one.three = null;
		two.one = null;
		two.two = null;
		two.three = null;
		three.one = null;
		three.two = null;
		three.three = null;
		four.one = six;
		four.two = null;
		four.three = null;
		five.one = null;
		five.two = null;
		five.three = null;
		six.one = null;
		six.two = null;
		six.three = null;
		
		System.out.println("两个节点的最低公共祖先:"+p.GetLowestCommonAncestor(root1,six,two).val);

	}
	private TreeNode GetLowestCommonAncestor(TreeNode root1, TreeNode node1, TreeNode node2) {
		// TODO Auto-generated method stub
		
		if(root1 == null || node1 == null || node2 == null)
			return null;
		
		Stack<TreeNode> path1 = new Stack<TreeNode>();
		boolean flag1 = GetThePathOfTheNode(root1,node1,path1);
		//树上没有node1节点
		if(!flag1) 
			return null;
		
		Stack<TreeNode> path2 = new Stack<TreeNode>();
		boolean flag2 = GetThePathOfTheNode(root1,node2,path2);
		//树上没有node2节点
		if(!flag2) 
			return null;
		
		//让两个路径等长
		if(path1.size() > path2.size())
			while(path1.size() != path2.size())
				path1.pop();
		else
			while(path1.size() != path2.size())
				path2.pop();
		
		//若path1 == path2  表示  比较的是stack1的地址与stack2的地址是都相同
		// 若相等      返回true 否则false
		if(path2 == path1)//表示path1和path2指向同一个地址空间     表示两个节点在一条路径上??
		{
			path1.pop();
			return path1.pop();
		}else {
			TreeNode p = path1.pop();
			TreeNode q = path2.pop();
			
			while(p != q) {
				p = path1.pop();
				q = path2.pop();
			}
			//从目标节点向上遍历,返回第一个相等的(公共祖先)
			return p;	
		}
			
	}
	
	//获取根节点到node节点的路径   从根节点开始自上而下的将路径压入栈中
	private boolean GetThePathOfTheNode(TreeNode root1, TreeNode node, Stack<TreeNode> path) {
		// TODO Auto-generated method stub
		//将根节点压入栈内
		path.push(root1);
		//直到在树中找到node节点后  才会结束递归  并逐层返回递归结果
		if(root1 == node)
			return true;
		boolean found = false;
		if(root1.one != null)
			found = GetThePathOfTheNode(root1.one,node,path);
		
		if(!found && root1.two != null)
			found = GetThePathOfTheNode(root1.two,node,path);
		
		if(!found && root1.three != null)
			found = GetThePathOfTheNode(root1.three,node,path);
		
		if(!found)
			path.pop();
		
		return found;
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值