剑指offer2_中等

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;
    }
资源下载链接为: https://pan.quark.cn/s/c705392404e8 在本项目中,我们聚焦于“天池-零基础入门数据挖掘-心跳信号分类预测-EDA分析全过程-代码.rar”这一主题。该压缩包涵盖了一次针对心跳信号分类预测的数据挖掘实践,涉及数据的初步探索性分析(Exploratory Data Analysis, EDA)以及相关代码。 “天池”通常指阿里巴巴天池大数据竞赛平台,这是一个提供各类数据竞赛的平台,旨在助力数据科学家和初学者提升技能并解决实际问题。此数据挖掘任务可能是一项竞赛项目,要求参赛者对心跳信号进行分类预测,例如用于诊断心脏疾病或监测健康状况。EDA是数据分析的关键环节,其目的是通过可视化和统计方法深入了解数据的特性、结构及潜在模式。项目中的“task2 EDA.ipynb”很可能是一个 Jupyter Notebook 文件,记录了使用 Python 编程语言(如 Pandas、Matplotlib 和 Seaborn 等库)进行数据探索的过程。EDA 主要包括以下内容:数据加载,利用 Pandas 读取数据集并检查基本信息,如行数、列数、缺失值和数据类型;描述性统计,计算数据的中心趋势(平均值、中位数)、分散度(方差、标准差)和分布形状;可视化,绘制直方图、散点图、箱线图等,直观呈现数据分布和关联性;特征工程,识别并处理异常值,创建新特征或对现有特征进行转换;相关性分析,计算特征之间的相关系数,挖掘潜在关联。 “example.html”可能是一个示例报告或结果展示,总结了 EDA 过程中的发现,以及初步模型结果,涵盖数据清洗、特征选择、模型训练和验证等环节。“datasets”文件夹则包含用于分析的心跳信号数据集,这类数据通常由多个时间序列组成,每个序列代表一个个体在一段时间内的 ECG 记录。分析时需了解 ECG 的生理背景,如波
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值