array专题7

714 Best Time to Buy and Sell Stock with Transaction Fee

思路

首先是暴力枚举。考虑在第idx天能做的操作:买?卖?不操作?

/**
     * 暴力枚举
     * 
     * @param prices
     * @param fee
     * @return
     */
    public int maxProfitV99(int[] prices, int fee) {
        return dfs(prices, 0, 0, fee, 0);
    }

    /**
     ** 考虑在第idx天是买?卖?不操作?
     **/
    private int dfs(int[] prices, int idx, int buyed, int fee, int profit) {
        if (idx >= prices.length) {
            return profit;
        }
        int max = Integer.MIN_VALUE;
        if (buyed == 0) {
            // 买
            max = Math.max(max, dfs(prices, idx + 1, 1, fee, profit - prices[idx]));
        } else {
            // 卖
            max = Math.max(max, dfs(prices, idx + 1, 0, fee, profit + prices[idx] - fee));
        }
        // 不操作
        max = Math.max(max, dfs(prices, idx + 1, buyed, fee, profit));
        return max;
    }

接着:用动态规划的思路来解决

关键是找到递归方程。定义:hold[i]是在第i天持有股票的状态下的最大收益;sold[i]是在第i天卖掉股票的状态下的最大收益; 方程式:hold[i] = Math.max(hold[i-1],sold[i-1]-prices[i])//第i-1天就持有股票,或者第i天购买股票。sold[i] = Math.max(hold[i-1]+prices[i]-fee,sold[i-1])。大家能看到上面的暴力搜索的思路并不完全浪费。在找递归方程中起了作用:hold[i]的计算可能是前一天已经hold,或者是前一天卖了,今天买入。sold[i]的计算可能是前一天已经hold今天卖,或者是前一天已经sold。初始化:hold[0] = -prices[0];sold[0] = 0。

public int maxProfit(int[] prices, int fee) {
        int n = prices.length;
        if(n<2) return 0;
        int[] hold = new int[prices.length];
        int[] sold = new int[prices.length];
        hold[0] = -prices[0];
        sold[0] = 0;
        for(int i=1;i<prices.length;i++){
            hold[i] = Math.max(hold[i-1],sold[i-1]-prices[i]);
            sold[i] = Math.max(hold[i-1]+prices[i]-fee,sold[i-1]);
        }
        return Math.max(hold[n-1], sold[n-1]);
    }

还可以用贪心

关键是选择一个能不能卖掉股票的点,重新开始寻找买入机会。例如序列 1 3 2 8 ,如果发现2<3,就完成交易买1卖3,此时,由于fee=2,那么收益=(3-1-fee)+(8-2-fee) < (8-1-fee),说明卖早了。令maxP是当前最大的price,当(maxP-prices[i]>=fee),时则可以在maxP处卖出,且不会存在早卖的情况。
证明:不明白。

public int maxProfitV3(int[] prices, int fee) {
        int n = prices.length;
        if (n < 2)
            return 0;
        int maxP = prices[0];
        int minP = prices[0];
        int curProfit = 0;
        int profit = 0;
        for (int i = 1; i < prices.length; i++) {
            maxP = Math.max(maxP, prices[i]);
            minP = Math.min(minP, prices[i]);
            curProfit = Math.max(curProfit, prices[i] - minP - fee);
            if(maxP - prices[i]>=fee){
                profit += curProfit;//在maxP处交易
                maxP = prices[i];
                minP = prices[i];
                curProfit = 0 ;
            }
        }
        return profit+curProfit;
    }

代码

376 Wiggle Subsequence

思路:用贪心或者动态规划可以解决。
代码

84 Largest Rectangle in Histogram

思路:首先是暴力枚举。 枚举矩形的左右边界。
学习:接着还是暴力枚举,只是这次枚举的是矩形的最低高度。对于一个heights[i],算一下能够到达的最左边的下标和最右边的下标。复杂度还是没降低。在这次枚举过程中会发现可以执行跳跃,省掉一些计算。例如找左边最远:如果当前设置变量j = i;如果heights[i]<=heights[j-1],那直接跳到left[j+1];j=left[j-1],循环再继续判断。例如找右边最远:如果当前设置变量j = i;如果heights[i]<=heights[j+1],那直接跳到right[j+1];j=right[j+1],循环再继续判断。这种跳跃的思想,是学习到的。
代码

621 Task Scheduler

思路:每次应该选择符合条件的剩余总数多的那个任务。符合条件是指符合间隔条件。需要一个map,计算每个任务的数量;需要第二个map,记录当前状态下还有多少个时间间隔。就形成了第一版本的代码。在这个过程中,我没有考虑n=0,没有考虑 每个任务数量不同的情况。是后来才加上的代码。当然代码是超时的。
学习:计算每个不同任务的数量。每次小循环i从1到n,优先选择数量多的不同任务。这里很关键的地方是:只与不同任务的数量有关系,而与任务名称没有关系。用一个一个的数字表示不同的任务。这是我之前一直转不过弯的地方。例如 3个A 1个B 1个C 1个D 1个E ,n=2,最好的顺序是A->B->C->A->D->E->A。第一个小循环用数字表示就是 3-1,1-1,1-1;第二个小循环用数字表示就是2-1,1-1,1-1,第3个小循环是:1-1,因为数组为空,退出。
该思路的难点是处理小循环,注意小循环退出的条件。该思路可以用数组、优先队列两种方法实现。
代码

斐波那契数列

斐波那契数列的计算可以用动态规划计算,也可以用矩阵计算,有 O(logn) O ( l o g n ) 的时间复杂度。
算法去冗余有两种角度:时间冗余和空间冗余。去时间冗余,就是利用空间,将本次计算结果存下来,下次用的时候直接取。 F(n)=F(n1)+F(n2) F ( n ) = F ( n − 1 ) + F ( n − 2 ) ,在这个式子中就有重复计算,可以用数组f[i]保存是i时候的结果。空间冗余,就看本次计算相关因素,只需要记录相关因素。还是 F(n)=F(n1)+F(n2) F ( n ) = F ( n − 1 ) + F ( n − 2 ) ,不需要记录f[0],f[1],只需要记录f[n-1],f[n-2],两个变量就可以。
动态规划时间复杂度:状态个数*(每个状态计算的时间复杂度);空间复杂度:数组大小。
动态规划解题套路:
1 设计暴力算法,找到冗余;
2 设计并存储状态(一维、二维、三维数组)
3 递归式(状态转移方程)
4 自底向上计算

48 Rotate Image

思路:不多写了。感叹一下自己没想到可以通过多个步骤得到旋转后的矩阵。
代码

153 Find Minimum in Rotated Sorted Array

学习:主要还是要观察所求值的特征。自己总结特征。最小元素的特点有两个:1 如果是旋转元素,那么 nums[min]<nums[min1] n u m s [ m i n ] < n u m s [ m i n − 1 ] ;2 如果不是旋转元素,那么它应该是nums[0]。所以可以使用二分查找法:如果 nums[mid]<nums[mid1] n u m s [ m i d ] < n u m s [ m i d − 1 ] ,则就是最小元素;否则如果nums[mid]>nums[start] && nums[mid]>nums[end] nums[mid]>nums[start] && nums[mid]>nums[end] ,那最小元素在右侧;否则在左侧。
代码

560 Subarray Sum Equals K

思路:最近看了动态规划的视频,知道一言不合就暴力搜索。子数组,那可能的枚举就是从0到1,2,3,….从1到2,3,…..。计算这些子数组的和是不是等于k。不过我遇到的几个问题是:1,开始是用递归写的,出现栈溢出,用循环解决。2 没有考虑负数的情况,在sum>=k的时候就break。3 没有考虑[0,0,0,0,0] 这种情况,在以i为开始,找到和为k的情况就break。时间复杂度O( n2 n 2 )。
学习:子数组的和就是指从[i,j]的和。sum[i,j] = sum[0,j]-sum[0,i-1]。
代码

795 Number of Subarrays with Bounded Maximum

思路:分析题意要求每个子数组的最大值maxVal必须满足 maxVal>=L m a x V a l >= L and maxVal<=R m a x V a l <= R maxVal<=R m a x V a l <= R 可以推出子数组中每个元素 <=R <= R <script type="math/tex" id="MathJax-Element-11"><=R</script>。
子数组的暴力枚举:以i为起始,可以行程[i][i,i+1],[i,i+2]….[i,n-1]个子数组。在枚举子数组的时候把不符合条件的去掉,计数有效子数组即可。时间复杂度O(n^2)。
学习:改为O(n)。为什么不是O(nlogn)。因为一般logn需要有二分,而二分一般涉及到排序。本题是不能排序的。所以应该是O(n)。要想O(n)就应该是一个一个元素遍历,或者根据需要多遍历几次。
遍历每个元素i,会发现,如果 A[i]>=LA[i]<=R A [ i ] >= L 并 且 A [ i ] <= R
数组[2,1,4,3] L=2,R=3
下标0: A[i]>=LA[i]<=R A [ i ] >= L 并 且 A [ i ] <= R 增加个数1
下标1: A[i]<L A [ i ] < L 自己不能单独成为子数组,可以追加在前面的子数组后面。所以前面有几个子数组这里就增加几。
下标2: A[i]>R A [ i ] > R 不增加
下标3: A[i]>=LA[i]<=R A [ i ] >= L 并 且 A [ i ] <= R ,增加1个。因为前一个元素超出范围了,只能从下标3开始计算。
代码

162 Find Peak Element

思路:找极值点。之前在贪心里面有一道题比这个还要难。思路就是找到一个元素i 如果 (nums[i]-nums[i-1])*(nums[i+1]-nums[i])<0,就找到了极值点的下标i。
学习:二分查找。这道题目居然也可以用二分。一直以为不排序的数组没法用二分查找。原因是这样的。只要找到一个极值点即可。在达到极值点前或者过了极值点之后是有序的。
ifnums[i1]<nums[i]<nums[i+1] i f n u m s [ i − 1 ] < n u m s [ i ] < n u m s [ i + 1 ] ,那么极值点一定在i+1,i+2…n-1中。
ifnums[i1]>nums[i]>nums[i+1] i f n u m s [ i − 1 ] > n u m s [ i ] > n u m s [ i + 1 ] ,那么极值点一定在[0,1,…i-1]中
ifnums[i1]<nums[i]>nums[i+1] i f n u m s [ i − 1 ] < n u m s [ i ] > n u m s [ i + 1 ] ,那么极值点就是i
所以学习到局部有序,也可以用二分查找。
代码

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值