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