1.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
class Solution {
public Map<Integer,Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder==null||preorder.length==0){
return null;
}
// 构造哈希映射,帮助我们快速定位根节点
this.map=new HashMap<>();
for(int i=0;i<inorder.length;i++){
map.put(inorder[i],i);
}
TreeNode root=process(preorder,inorder,0,preorder.length-1,0,inorder.length-1);
return root;
}
public TreeNode process(int[] preorder, int[] inorder,int preLeft,int preRight,int inLeft,int inRight){
if(preRight<preLeft){
return null;
}
TreeNode root=new TreeNode(preorder[preLeft]);// 前序遍历中的第一个节点就是根节点
int index=map.get(root.val);// 在中序遍历中定位根节点
int count=index-inLeft;// 得到左子树中的节点数目
root.left=process(preorder,inorder,preLeft+1,preLeft+count,inLeft,index-1);// 递归地构造左子树,并连接到根节点
root.right=process(preorder,inorder,preLeft+count+1,preRight,index+1,inRight);// 递归地构造右子树,并连接到根节点
return root;
}
}
https://www.bilibili.com/video/BV1f741197Rx b站讲解
https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-by-leetcode-s/ leetcode题解
2.矩阵中的路径
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
public boolean exist(char[][] board, String word) {
int m=board.length;
int n=board[0].length;//二维数组的长和宽
char[] chars=word.toCharArray();
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(dfs(board,chars,i,j,0)){//调用递归函数,i,j为当前位置
return true;
}
}
}
return false;
}
public boolean dfs(char[][] board,char[] chars,int i,int j,int k){
if(i<0||i>=board.length||j<0||j>=board[0].length){//若当前位置越界则返回false
return false;
}
if(board[i][j]!=chars[k]){ //若矩阵当前位置不等于数组当前位置,则返回false
return false;
}
if(k==chars.length-1){ //到了数组最后一个位置,说明数组之前都与矩阵匹配
return true;
}
board[i][j]='#'; //避免重复访问,将当前位置标为已访问
boolean res=dfs(board,chars,i+1,j,k+1) || dfs(board,chars,i-1,j,k+1) || dfs(board,chars,i,j+1,k+1) || dfs(board,chars,i,j-1,k+1); //向4个方向去递归,只要有一个返回true就行
board[i][j]=chars[k]; //万一这个位置后序的都不行,到上一个位置,要还原当前位置
return res;
}
3.机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
public boolean[][] visit;
public int movingCount(int m, int n, int k) {
visit=new boolean[m][n]; //记录访问过的位置
int res=dfs(m,n,0,0,k); //递归
return res;
}
public int getSum(int i,int j){ //返回i和j的各个数位之和
int sumi=0;
while(i!=0){
sumi=i%10+sumi;
i=i/10;
}
int sumj=0;
while(j!=0){
sumj=j%10+sumj;
j=j/10;
}
return sumi+sumj;
}
public int dfs(int m,int n,int i,int j,int k){//递归函数
if(i<0||i>=m||j<0||j>=n||visit[i][j]){ //若i,j越界或者访问过,则返回0
return 0;
}
if(getSum(i,j)>k){ //如果各个数位之和大于k则返回0
return 0;
}
visit[i][j]=true; //记录该位置被访问过
return dfs(m,n,i+1,j,k)+dfs(m,n,i,j+1,k)+1;
}
4.剪绳子1
(1)暴力递归
public int cuttingRope(int n) {
return dfs(n);
}
public int dfs(int n){
if(n==1){//若绳子长度为1,则直接返回
return 1;
}
int result=0;
for(int i=1;i<n;i++){//i表示剪掉绳子的长度,因为必须剪一刀,所以从1开始
//i*dfs(n-i) 当前剪的*剩余长度再去剪 i*(n-i)剩余的就不剪了 max 留下大的
result=Math.max(result,Math.max(i*dfs(n-i),i*(n-i)));
}
return result;
}
(2)动态规划(根据递归改的)
public int cuttingRope(int n) {
if(n==0){
return 0;
}
int[] dp=new int[n+1];
dp[0]=0;
dp[1]=1;
for(int i=2;i<dp.length;i++){//给数组赋值
for(int j=1;j<i;j++){ //注意是小于i
dp[i]=Math.max(dp[i],Math.max(j*dp[i-j],j*(i-j))); //注意是i-j
}
}
return dp[n];
}
5.剪绳子2
动态规划不能用,会出溢出的错误,只能用贪心
分成3个3个是最优的,但是最后剩下4的时候要直接乘4,不能再分了
如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1
如果 n == 4,返回4
如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段;每次乘法操作后记得取余就行
以上2和3可以合并
//因为题目给的条件是n>=2,所以n=0,1的边界条件不考虑
if(n<4){
return n-1;
}
long res=1; //结果用long存,int存不下
while(n>4){ //当n<=4的时候停止,如果最后剩下4的时候要直接乘4,不能再分了
res=(res*3)%1000000007;
n-=3;
}
return (int)(res*n%1000000007);//记住先取余在强制类型转换
https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/jian-zhi-offer-14-ii-jian-sheng-zi-iihua-r5op/
6.数值的整数次方
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
public double myPow(double x, int n) {
if(x==0){
return 0;
}
long b=n; //为什么要用long呢?因为当n=-2147483648的时候,转为正数会越界
if(b<0){
x=1/x;
b=-b;
}
double res=1;
while(b!=0){ //计算过程如上图
if((b&1)==1) //注意(b&1)要加括号
res=res*x;
x*=x;
b=b>>1;
}
return res;
}
7.表示数字的字符串
取巧的办法(原办法太难)
public boolean isNumber(String s) {
if(s==null||s.length()==0){
return false;
}
s=s.trim();//去除掉两边空格
try{
double a =Double.parseDouble(s); //看能不能转为double 若出现异常,直接返回false
}catch(Exception e){
return false;
}
char end =s.charAt(s.length()-1); //得到最后一个字符
return (end>='0'&&end<='9')||(end=='.'); //最后一个字符若为数字或点的话都算true
}
8.树的子结构
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
3
/ \
4 5
/
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) { //判断是不是子树
if(A==null || B==null){ //当A为空或B为空的时候,根据题意是不行的
return false;
}
/*
当前以A为头结点的树结构与B树结构相同
A的左子树中包含B
A的右子树中包含B
*/
return isSame(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B) ;
}
//判断当前树与B树结构相同
public boolean isSame(TreeNode a,TreeNode b){
if(b==null) return true; //判断结构相等时,b是可以为空的
if(a==null) return false; //当b不为空时,a为空说明结构不匹配
/*
当前节点值相同
左子树结构相同
右子树结构相同
*/
return a.val==b.val && isSame(a.left,b.left) && isSame(a.right,b.right);
}
}
https://www.bilibili.com/video/BV1WE411P7rc?from=search&seid=6357020164756765376
9.栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
算法流程:
初始化: 辅助栈 stack,弹出序列的索引 j;
遍历压栈序列: 各元素记为num ;
元素 num 入栈;
循环出栈:若 stack的栈顶元素 = 弹出序列元素 popped[j] ,则执行出栈与 j++ ;
返回值: 若 stack为空,则此弹出序列合法。
public boolean validateStackSequences(int[] pushed, int[] popped) {
int j=0;
Stack<Integer> stack=new Stack();
for(int num : pushed){
stack.push(num);
while(!stack.isEmpty()&&stack.peek()==popped[j]){
stack.pop();
j++;
}
}
return stack.isEmpty();
}
10.III. 从上到下打印二叉树 III
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if(root==null){
return res;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
List<Integer> tempList=new ArrayList<>();
int size=queue.size();
for(int i=0;i<size;i++){
TreeNode tempNode=queue.poll();
tempList.add(tempNode.val);
if(tempNode.left!=null){
queue.add(tempNode.left);
}
if(tempNode.right!=null){
queue.add(tempNode.right);
}
}
if(res.size()%2==1){ //最重要的一步,若是奇数层则反转列表,加入最终结果,其余就都是层序遍历的步骤了
Collections.reverse(tempList);
}
res.add(tempList);
}
return res;
}
11.二叉搜索树的后序遍历序列
后序遍历定义: [ 左子树 | 右子树 | 根节点 ] ,即遍历顺序为 “左、右、根” 。
二叉搜索树定义: 左子树中所有节点的值 < 根节点的值;右子树中所有节点的值 > 根节点的值;其左、右子树也分别为二叉搜索树。
class Solution {
public boolean verifyPostorder(int[] postorder) {
if(postorder==null || postorder.length==0){
return true;
}
return verify(0,postorder.length-1,postorder);
}
//判断数组postorder[left:right] 是否满足搜索二叉树
public boolean verify(int left,int right,int[] postorder){
if(left>=right){
return true;
}
int index=left;//向右遍历用
int mid=left; //保存左右子树边界用
//确保了postorder[left:mid] 都是比根节点小的
while(postorder[index]<postorder[right]){
index++;
}
mid=index-1;
//检查剩余部分是否比根节点大
while(postorder[index]>postorder[right]){
index++;
}
//若index==right 则说明满足左子树的值都比根节点小,右子树的值都比根节点大
return index==right && verify(left,mid,postorder) && verify(mid+1,right-1,postorder);
}
}
12.二叉树中和为某一值的路径(回溯法)
class Solution {
List<List<Integer>> res=new ArrayList<>(); //存放最终的结果
List<Integer> path=new ArrayList<>(); //存放路径
public List<List<Integer>> pathSum(TreeNode root, int target) {
dfs(root,target);
return res;
}
public void dfs(TreeNode root,int target){
if(root==null){ //如果节点为null直接返回
return ;
}
path.add(root.val); //路径中加入该值
target-=root.val; //目标减去该节点的值
if(target==0 && root.left==null && root.right==null){ //说明该叶子节点符合要求
res.add(new ArrayList<>(path)); //注意这里要新建一个列表,不能操作原来的列表
}
dfs(root.left,target);
dfs(root.right,target);
path.remove(path.size()-1); //回溯回来的时候,路径中要减去该节点
}
}
https://leetcode-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof/solution/mian-shi-ti-34-er-cha-shu-zhong-he-wei-mou-yi-zh-5/
https://www.bilibili.com/video/BV1Nv4y1f74Z
13.复杂链表的复制(拼接与拆分)
public Node copyRandomList(Node head) {
if(head==null){
return null;
}
//1.拼接链表,将链表拼成A A’ B B’的形式
Node cur=head;
while(cur!=null){
Node newNode=new Node(cur.val);
newNode.next=cur.next;
cur.next=newNode;
cur=newNode.next;
}
//2.连接random指针
cur=head;
while(cur!=null){
if(cur.random!=null){
cur.next.random=cur.random.next;
}
cur=cur.next.next; //注意这里要写到if外面
}
//3.拆开
cur=head;
Node newHead=cur.next;
Node temp=cur.next;
while(cur.next!=null){
cur.next=temp.next;
cur=temp;
temp=cur.next; //写得时候忘写了这句赋值
}
return newHead;
}
14.把数组排成最小的数
public String minNumber(int[] nums) {
if(nums==null||nums.length==0){
return null;
}
//(1)将数字数组放到字符串数组中
String[] ss=new String[nums.length];
for(int i=0;i<nums.length;i++){
ss[i]=String.valueOf(nums[i]);
}
//(2)最重要的一步:按字典序排好字符串数组
Arrays.sort(ss,new Comparator<String>(){
@Override
public int compare(String s1,String s2){
return (s1+s2).compareTo(s2+s1);
}
});
//(3)拼接字符串
StringBuilder sb=new StringBuilder();
for(int i=0;i<ss.length;i++){
sb.append(ss[i]);
}
return sb.toString();
}
15.把数字翻译成字符串
class Solution {
public int translateNum(int num) {
String s=String.valueOf(num);
return dps(s.toCharArray(),0);
}
//i之前的位置,如何转化已经做过决定了
//i...有多少种转化的结果
public int dps(char[] s,int i){
if(i==s.length){
return 1;
}
if(s[i]=='1'){//i自己作为单独的部分,后续有多少种方法
int res=dps(s,i+1);
if(i+1<s.length){
res+=dps(s,i+2);//(i和i+1)作为单独的部分,后续有多少种方法
}
return res;
}
if(s[i]=='2'){
int res=dps(s,i+1);//i自己作为单独的部分,后续有多少种方法
if(i+1<s.length&&s[i+1]>='0'&&s[i+1]<='5'){
res+=dps(s,i+2);//(i和i+1)作为单独的部分并且没有超过26,后续有多少种方法
}
return res;
}
return dps(s,i+1);
}
16.礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
另一种想法:
设 f(i, j)为从棋盘左上角走至单元格 (i ,j) 的礼物最大累计价值,易得到以下递推关系:f(i,j) 等于 f(i,j-1) 和 f(i-1,j)中的较大值加上当前单元格礼物价值 grid(i,j)。
f(i,j) = max[f(i,j-1), f(i-1,j)] + grid(i,j)
因此,可用动态规划解决此问题,以上公式便为转移方程。
https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof/solution/mian-shi-ti-47-li-wu-de-zui-da-jie-zhi-dong-tai-gu/
(1)暴力递归(会超时)
public int maxValue(int[][] grid) {
return dfs(grid,0,0);
}
public int dfs(int[][] grid,int i,int j){
if(i==grid.length-1&&j==grid[0].length-1){
return grid[i][j];
}
if(i==grid.length-1){
return grid[i][j]+dfs(grid,i,j+1);
}
if(j==grid[0].length-1){
return grid[i][j]+dfs(grid,i+1,j);
}
return grid[i][j]+Math.max(dfs(grid,i+1,j),dfs(grid,i,j+1));
}
(2)由暴力递归改的动态规划
public int maxValue(int[][] grid) {
int m=grid.length;
int n=grid[0].length;
int[][] dp=new int[m][n];
dp[m-1][n-1]=grid[m-1][n-1];
for(int j=n-2;j>=0;j--){
dp[m-1][j]=grid[m-1][j]+dp[m-1][j+1];
}
for(int i=m-2;i>=0;i--){
dp[i][n-1]=grid[i][n-1]+dp[i+1][n-1];
}
for(int i=m-2;i>=0;i--){
for(int j=n-2;j>=0;j--){
dp[i][j]=grid[i][j]+Math.max(dp[i+1][j],dp[i][j+1]);
}
}
return dp[0][0];
}
17.数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
算法:先对所有数字进行一次异或,得到两个出现一次的数字的异或值。
在异或结果中找到任意为 1的位。
根据这一位对所有的数字进行分组。
在每个组内进行异或操作,得到两个数字。
public int[] singleNumbers(int[] nums) {
//先对所有数字进行一次异或,得到两个出现一次的数字的异或值。
int res=0;
for(int num : nums){
res=res^num;
}
//在异或结果中找到任意为 1的位。
int div=1;
while((res&div)==0){
div=div<<1;
}
//根据这一位对所有的数字进行分组。在每个组内进行异或操作,得到两个数字。
int a=0;
int b=0;
for(int num : nums){
if((num&div)==0){
a=a^num;
}else{
b=b^num;
}
}
return new int[]{a,b};
}
18.最长不含重复字符的子字符串
前一位字符的最长子串和该位与上一次出现的距离取最小值
public int lengthOfLongestSubstring(String s) {
if(s==null||s.length()==0){
return 0;
}
char[] chars=s.toCharArray();
int[] dp=new int[s.length()];
dp[0]=1;
for(int i=1;i<s.length();i++){
//找到该位置字符上一次出现的位置
int j=i-1;
for(;j>=0;j--){
if(chars[j]==chars[i]){
break;
}
}
//取最小值
if(j==-1){
dp[i]=Math.min(dp[i-1]+1,i+1);
}else{
dp[i]=Math.min(dp[i-1]+1,i-j);
}
}
//得到最大的值
int max=1;
for(int i=0;i<dp.length;i++){
max=Math.max(dp[i],max);
}
return max;
}
19.丑数
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
思路:一个丑数必然是由比它小的丑数2或3或*5得到的
public int nthUglyNumber(int n) {
int[] dp=new int[n];
dp[0]=1;
int p2=0;
int p3=0;
int p5=0;
for(int i=1;i<n;i++){
dp[i]=Math.min(Math.min(dp[p2]*2,dp[p3]*3),dp[p5]*5);
if(dp[i]==(dp[p2]*2)){//这时候p2指向的数就没有利用价值了,++
p2++;
}
if(dp[i]==(dp[p3]*3)){
p3++;
}
if(dp[i]==(dp[p5]*5)){
p5++;
}
}
return dp[n-1];
}
20.请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:
输入:
[“MaxQueue”,“pop_front”,“max_value”]
[[],[],[]]
输出: [null,-1,-1]
解:从队列尾部插入元素时,我们可以提前取出队列中所有比这个元素小的元素,使得队列中只保留对结果有影响的数字。这样的方法等价于要求维持队列单调递减,即要保证每个元素的前面都没有比它小的元素。
class MaxQueue {
Queue<Integer> queue;
Deque<Integer> dueue; //注意双端队列的写法
public MaxQueue() {
queue=new LinkedList<>();
dueue=new LinkedList<>();
}
public int max_value() {
if(queue.isEmpty()) return -1;
return dueue.getFirst(); //双端队列队头位置始终放的是最大值
}
public void push_back(int value) {
queue.add(value);
while(!dueue.isEmpty()&&dueue.getLast()<value){//提前取出队列中所有比这个元素小的元素
dueue.removeLast();
}
dueue.addLast(value);
}
public int pop_front() {
// if(queue.isEmpty()) return -1;
// if(dueue.getFirst()==queue.peek()){//注意不能这么比较相等,因为这样拿出来的是对象,超过常量池(127)的值之后就需要使用equals比较
// dueue.removeFirst();
// }
// return queue.remove();
if(queue.isEmpty()) return -1;
int ans=queue.remove();
if(dueue.getFirst()==ans){
dueue.removeFirst();
}
return ans;
}
}
https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof/solution/mian-shi-ti-59-ii-dui-lie-de-zui-da-zhi-by-leetcod/
https://www.bilibili.com/video/BV1Zc411h7b5?from=search&seid=14625314399424476302
21.给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
示例:
输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
解法:得到i位置之前的乘积,i位置之后的乘积,再相乘
public int[] constructArr(int[] a) {
int [] mulPre=new int[a.length]; //放着i位置之前的乘积
int [] mulBack=new int[a.length]; //放着i位置之后的乘积
int n=a.length;
for(int i=0;i<n;i++){ //初始化i位置之前的乘积
if(i==0){
mulPre[i]=1;
}else{
mulPre[i]=mulPre[i-1]*a[i-1];
}
}
for(int i=n-1;i>=0;i--){//初始化i位置之后的乘积
if(i==n-1){
mulBack[i]=1;
}else{
mulBack[i]=mulBack[i+1]*a[i+1];
}
}
int[] res=new int[n];
for(int i=0;i<n;i++){ //得到最终的结果
res[i]=mulPre[i]*mulBack[i];
}
return res;
}
https://www.bilibili.com/video/BV1xV411f773?from=search&seid=1384944034247613226 视频中思路是对的,编码有些逻辑小错误
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 1:股票的最大利润
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
(1)暴力
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
if(prices.length==1){
return 0;
}
int max=0;
for(int i=0;i<prices.length-1;i++){
for(int j=i+1;j<prices.length;j++){
if(prices[j]>prices[i]){
max=Math.max(max,prices[j]-prices[i]);
}
}
}
return max;
}
(2)维护一个最小值变量
public int maxProfit(int[] prices) {
if(prices==null||prices.length<=1){
return 0;
}
int res=0;
int min=Integer.MAX_VALUE;
for(int i=0;i<prices.length;i++){
min=Math.min(prices[i],min);
res=Math.max(prices[i]-min,res);
}
return res;
}