JAVA入门算法题(十二)

版权声明:很高兴认识你,我叫邵龙飞 原创文章,转载请注明 https://blog.csdn.net/qq_37482202/article/details/88927472

一、最大利润

题目:

/**
 *给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
 * 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
 * 注意你不能在买入股票前卖出股票。
 * 示例 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。
 */

 最简单粗暴的方法:把所有的买法都试一遍,得出最大值

两层for循环,外层控制买入,内层控制卖出

    public int method1(int[] prices) {
        int length=prices.length;
        if(length<2) return 0;
        int res=0;
        for (int i=0;i < length; i++) {
            for (int j =i+1; j <length ; j++) {
                if(prices[i]<prices[j]){
                    res=Math.max(res,prices[j]-prices[i]);
                }
            }
        }
        return res;
    }

聪明一些的方法:利用快慢指针的思想,快指针控制卖出,慢指针控制买入

如果当前价格比慢指针的价格还低,移动慢指针到当前

不断计算利润,如果当前利润高于历史最高利润,替换历史最高利润

    public int method2(int[] prices){
        if(prices == null || prices.length == 0){
            return 0;
        }
        int currMinPrice = prices[0];
        int maxProfit = 0;
        int currProfit = 0;
        for(int i = 1; i < prices.length; i++){
            if(prices[i] < currMinPrice){
                currMinPrice = prices[i];
            }
            currProfit = prices[i] - currMinPrice;
            if(currProfit > maxProfit){
                maxProfit = currProfit;
            }
        }
        return maxProfit;
    }

把刚才那道题的限制条件改一下,可以无限次的购买股票,计算最大利润

题目:

/**
 *给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
 * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
 * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
 * 示例 1:
 * 输入: [7,1,5,3,6,4]
 * 输出: 7
 * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
 *      随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
 * 示例 2:
 * 输入: [1,2,3,4,5]
 * 输出: 4
 * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
 *      注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
 *      因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
 * 示例 3:
 * 输入: [7,6,4,3,1]
 * 输出: 0
 * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
 */

 看起来比刚才还要简单

第一种思路:有钱就挣,计算每两天的利润,大于零就加和

    public static int method1(int[] prices) {
        int money=0;
        for (int i=0;i<prices.length-1;i++){
            int cha=prices[i+1]-prices[i];
            if (cha>0){
                money+=cha;
            }
        }
        return money;
    }

第二种思路:利用快慢指针,还是快指针控制卖,慢指针控制买,不断找价格曲线的谷和峰,在谷买,在峰卖。注意控制数组尾部。

    public static int method2(int[] prices) {
        if(prices == null || prices.length == 1){
            return 0;
        }
        int buy = 0;
        int sell =1;
        int profit = 0;
        while (sell < prices.length) {
            if (prices[buy] >= prices[sell]) {
                buy++;
                sell++;
            } else {
                if ((sell + 1 <= prices.length - 1) && (prices[sell] <= prices[sell + 1])) {
                    sell++;
                } else {
                    profit += (prices[sell] - prices[buy]);
                    buy = sell + 1;
                    sell = buy + 1;
                }
            }
        }
        return profit;
    }

代码优化:

   public static int method3(int[] prices) {
        if(prices==null || prices.length<=1) return 0;
        int i=0;
        int valley;
        int peak;
        int maxprofit=0;
        while(i<prices.length-1){
            while(i<prices.length-1 && prices[i]>=prices[i+1]){
                i++;
            }
            valley=prices[i];//最低点
            while(i<prices.length-1 && prices[i]<=prices[i+1]){
                i++;
            }
            peak=prices[i];//最高点
            maxprofit +=peak-valley;
        }
        return maxprofit;
    }

现在再改变一下条件,限制你购买股票的次数为两次,得出最大利润。

题目:

/**
 * 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
 * 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
 * 注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
 * 示例 1:
 * 输入: [3,3,5,0,0,3,1,4]
 * 输出: 6
 * 解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
 *      随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
 * 示例 2:
 * 输入: [1,2,3,4,5]
 * 输出: 4
 * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
 *      注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
 *      因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
 * 示例 3:
 * 输入: [7,6,4,3,1]
 * 输出: 0
 * 解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。
 */

这样就比较有难度了,你要考虑两次的买入卖出所赚得的利润之和最大,所以你要把两次买入卖出关联起来,在第一次买入卖出的基础上进行第二次交易。

你可以这样想,所有的减法都是加法,所有的买入都是卖出,减法是加了一个负数,买入是赚得了负数的钱

那么第一次买赚了-x1,总资金-x1,第一次卖赚了y1,总资金y1-x1,第二次买赚了-x2,总资金y1-x1-x2,第二次卖赚了y2,总利润y1+y2-x1-x2,我们的目的就是让每一次买每一次卖的利润都最大

    public static int method1(int[] prices) {
        int fstBuy = Integer.MIN_VALUE, fstSell = 0;
        int secBuy = Integer.MIN_VALUE, secSell = 0;
        for(int p : prices) {
            fstBuy = Math.max(fstBuy, -p);
            fstSell = Math.max(fstSell, fstBuy + p);
            secBuy = Math.max(secBuy, fstSell - p);
            secSell = Math.max(secSell, secBuy + p);
        }
        return secSell;
    }

二、回文字符串

题目:

/**
 * 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
 * 说明:本题中,我们将空字符串定义为有效的回文串。
 * 示例 1:
 * 输入: "A man, a plan, a canal: Panama"
 * 输出: true
 * 示例 2:
 * 输入: "race a car"
 * 输出: false
 */

 第一种思路:利用两个指针,从头尾开始比较,相同就移动,遇到非字母数字的字符就继续移动,不同返回false,相遇返回true

操作之前先把字符串转换成小写或者大写

需要注意连续特殊字符,全特殊字符等情况

    public static boolean method1(String s) {
        if (s.length()<2)return true;
        s=s.toLowerCase();
        int head=0;
        int foot=s.length()-1;
        while (head<foot){
            while (noLetter(s.charAt(head))&&head<s.length()-1){
                head++;
            }
            while (noLetter(s.charAt(foot))&&foot>0){
                foot--;
            }
            if (head<foot){
                if (s.charAt(head)!=s.charAt(foot)){
                    return false;
                }else {
                    head++;
                    foot--;
                }
            }
        }
        return true;
    }

    public static boolean noLetter(char a){
        return (a<'a'||a>'z')&&(a<48||a>57);
    }

第二种思路:遍历字符串,把字母和数字挑出来组成新的字符串,利用Sting的api完成判断

    public static boolean method2(String s) {
        if (s == null) return true;
        s = s.toLowerCase();
        int l = s.length();
        StringBuilder str = new StringBuilder(l);
        for (char c : s.toCharArray()) {
            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) {
                str.append(c);
            }
        }
        return str.toString().equals(str.reverse().toString());
    }

第三种思路:替换所有非字母数字的字符为空,使用while循环头尾进行比较

    public static boolean method3(String s) {
        if (s == null)    return true;
        String pure = s.toLowerCase().replaceAll("[^a-z0-9]", "");
        int i = 0, j = pure.length() - 1;
        while (i < j) {
            if (pure.charAt(i++) != pure.charAt(j--))
                return false;
        }
        return true;
    }

改动一下要求,你可以删除一个字符,判断能否使之成为回文字符串

/**
 * 给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
 * 示例 1:
 * 输入: "aba"
 * 输出: True
 * 示例 2:
 * 输入: "abca"
 * 输出: True
 * 解释: 你可以删除c字符。
 * 注意:
 * 字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。
 */ 

 这道题要注意的便是当检测到头尾字符不同时删除哪个字符的问题

可以进行递归判断

遇到不同的,删除左边和删除右边各自判断

    public static boolean method1(String s,boolean revival,int head,int foot) {
        while (head < foot && head < s.length() && foot > 0) {
            if (s.charAt(head) != s.charAt(foot)) {
                if (revival){
                    boolean left=false;
                    boolean right=false;
                    if (s.charAt(head + 1) == s.charAt(foot)) {
                        left=method1(s,false,head+1,foot);
                    }
                    if (s.charAt(head) == s.charAt(foot - 1)) {
                        right=method1(s,false,head,foot-1);
                    }
                    return left||right;
                }
                return false;
            }
            head++;
            foot--;
        }
        return true;
    }

转换为char[]操作,对删除左边和右边进一步判断,提高性能

    public static boolean method2(char[] ss, int left, int right, int flag) {
        for (; left <= right; left++, right--) {
            if (ss[left] != ss[right]) {
                flag++;
                if (flag == 2) {
                    return false;
                }
                if (ss[left+1] == ss[right] && ss[left] != ss[right-1]) {
                    left++;
                } else if (ss[left] == ss[right-1] && ss[left+1] != ss[right]) {
                    right--;
                } else if (ss[left] != ss[right-1] && ss[left+1] != ss[right]) {
                    return false;
                } else {
                    boolean leftcheck = method2(ss, left+1, right, flag);
                    boolean rightcheck = method2(ss, left, right-1, flag);
                    if (leftcheck || rightcheck) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        }
        return true;
    }

猜你喜欢

转载自blog.csdn.net/qq_37482202/article/details/88927472