Leetcode_486_PredictTheWinner_877_StoneGame_Medium

Leetcode_486_PredictTheWinner_877_StoneGame_Medium

/**
     * Leetcode486与Leetcode877说明:
     * 两题目解法几乎一样,可以看做一个题目,唯一不同的是877必需是差值大于0,二486只需要大于等于0即可。
     * 题目区别:486的nums长度可为奇数或偶数,且总和可以为偶数也可以为奇数。
     * 877对应的两人选择次数相同,总和肯定不同,即最终差值肯定不为0;
     * 而486对应的两人选择次数可能不相同,有一人可能多选择一个,最终差值可能为0.
     * 先介绍Leetcode877题目,仿照Leetcode486即可解。
     */

1、 Leetcode_877_StoneGame

(1)、问题描述


/***********Leetcode_877_StoneGame_Medium*******/
/**
* Leetcode_877_StoneGame_Medium
* Alex and Lee play a game with piles of stones.  There are an even number of piles
* arranged in a row, and each pile has a positive integer number of stones piles[i].
* The objective of the game is to end with the most stones.  The total number of stones is odd,
* so there are no ties.
* Alex and Lee take turns, with Alex starting first.  Each turn, a player takes
* the entire pile of stones from either the beginning or the end of the row.
* This continues until there are no more piles left, at which point the person with the most stones wins.
* Assuming Alex and Lee play optimally, return True if and only if Alex wins the game.
* <p>
* Example 1:
* Input: [5,3,4,5]
* Output: true
* Explanation:
* Alex starts first, and can only take the first 5 or the last 5.
* Say he takes the first 5, so that the row becomes [3, 4, 5].
* If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points.
* If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points.
* This demonstrated that taking the first 5 was a winning move for Alex, so we return true.
* Note:
* 2 <= piles.length <= 500
* piles.length is even.
* 1 <= piles[i] <= 500
* sum(piles) is odd.
* <p>


(2)、思路分析:


* 方法0:直接返回true即可。两人都是以最佳方式选择,且堆数为偶数,总和为奇数,因此谁先选择谁赢。
* 方法1:利用Min-Max递归解法,返回最大的相对差值。
* 时间复杂度为O(N^2),无法通过。
* 方法2:利用动态规划解决。dp[][]
* dp[start][end]表示start到end最佳选择的最大差值。
* 实际利用的是dp[][]的右上三角;按照对角线遍历,利用perLen来进行遍历填值。
* 首先最长对角线初始化为piles值;然后递推公式:
* dp[start][end] = Math.max(piles[start] - dp[start + 1][end], piles[end] - dp[start][end - 1]);
* 时间复杂度:O(N^2);
* 空间复杂度:O(N).
* 方法3:利用动态规划解决,降低空间复杂度。dp[N]
* 从递推公式dp[start][end] = Math.max(piles[start] - dp[start + 1][end], piles[end] - dp[start][end - 1])
* 看出dp[start][end]只与相邻左边元素和下边元素有关,即上一个对角线上的两个元素,
* 每次perLen循环就是从旧的对角线更新到新的对角线,且对角线长度逐渐降低,因此元素覆盖即可。
*/

(3)Java代码

// 方法0:直接返回true即可,Alex必定会赢
public boolean stoneGame1(int[] piles) {
    if (piles == null || piles.length == 0) return false;
    return true;
}

/**
* 方法1:Min-Max方法:当piles长度过大时,递归时间太长
* Leetcode提交超时不通过Time Limit Exceeded
* 时间复杂度:O(2^N),太大了
* 空间复杂度:O(N);递归深度为N
*/
public boolean stoneGame_Recursive(int[] piles) {
    if (piles == null || piles.length == 0) return false;
    return relativeMaxDiff(piles, 0, piles.length - 1) > 0;
}

/***
* 由于两人都是最佳选择:所以抽象一个方法,从start到end时选择相对差值最大的。
*
* 数组从start到end最佳选择下返回的最大差值
* 递归终止条件:start==end返回本身即可;
* 否则,对应两种选择:1、自己选择start,然后减去对手从start+1到end的最佳选择最大差值;
* 2、自己选择end,然后减去对手从start到end-1的最佳选择的最大差值。
* 两种选择中取max,即自身选择从start到end的最大差值。
*/
public int relativeMaxDiff(int[] piles, int start, int end) {
    if (start < 0 || end >= piles.length || start > end) return 0;//边界条件判断
    if (start == end) return piles[start];//递归终止条件
    return Math.max(piles[start] - relativeMaxDiff(piles, start + 1, end),
            piles[end] - relativeMaxDiff(piles, start, end - 1));
}

/**
* 方法2:Min-Max方法+DP:由于递归存在大量重复问题,利用dp[start][end]解决重复问题
* 时间复杂度:O(N^2)
* 空间复杂度:O(N^2);仅仅利用矩阵dp[][]的右上角。
*/
public boolean stoneGame_DP1(int[] piles) {
    if (piles == null || piles.length == 0) return false;
    int len = piles.length;
    int[][] dp = new int[len][len];
    //初始化
    for (int i = 0; i < len; i++) dp[i][i] = piles[i];
    int end;
    for (int perLen = 2; perLen <= len; perLen++) {
        for (int start = 0; start <= len - perLen; start++) {
            end = start + perLen - 1;
            dp[start][end] = Math.max(piles[start] - dp[start + 1][end], piles[end] - dp[start][end - 1]);
        }
    }
    return dp[0][len - 1] > 0;//大于0,获胜
}

/**
* 方法3:动态规划解法,降低空间复杂度
* 时间复杂度:O(N^2)
* 空间复杂度:O(N);仅仅利用矩阵dp[][]的反对角线。
*/
public boolean stoneGame(int[] piles) {//最佳
    if (piles == null || piles.length == 0) return false;
    int len = piles.length;
    int[] dp = new int[len];//相当于dp[][]的反对角线
    //初始化
    for (int i = 0; i < len; i++) dp[i] = piles[i];
    int end;
    for (int perLen = 2; perLen <= len; perLen++) {
        for (int start = 0; start <= len - perLen; start++) {
            end = start + perLen - 1;
            dp[start] = Math.max(piles[start] - dp[start + 1], piles[end] - dp[start]);
        }
    }
    return dp[0] > 0;
}

2、Leetcode_486_PredictTheWinner_Medium

(1)、问题描述

 /**
     * Leetcode_486_PredictTheWinner_Medium
     * 难度:Medium
     * 类型:Dame,DP动态规划
     * 问题介绍:
     * Given an array of scores that are non-negative integers.
     * Player 1 picks one of the numbers from either end of the array followed by the player 2 and
     * then player 1 and so on. Each time a player picks a number, that number will not be available
     * for the next player. This continues until all the scores have been chosen.
     * The player with the maximum score wins.
     * Given an array of scores, predict whether player 1 is the winner.
     * You can assume each player plays to maximize his score.
     * Example 1:
     * Input: [1, 5, 2]
     * Output: False
     * Explanation: Initially, player 1 can choose between 1 and 2.
     * If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2).
     * So, final score of player 1 is 1 + 2 = 3, and player 2 is 5.
     * Hence, player 1 will never be the winner and you need to return False.
     * Example 2:
     * Input: [1, 5, 233, 7]
     * Output: True
     * Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233.
     * Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win.
     * Note:
     * 1 <= length of the array <= 20.
     * Any scores in the given array are non-negative integers and will not exceed 10,000,000.
     * If the scores of both players are equal, then player 1 is still the winner.
     * <p>

(2)、思路分析

 * 思路分析:
     * 与877区别:返回的差值为0,也表示Player 1能赢。
     * 方法1:递归方法;
     * 方法2:动态规划,dp[len][len];
     * 方法3:动态规划,dp[len].

(3)、Java代码

 public boolean PredictTheWinner_Recursive(int[] nums) {
        if (nums == null || nums.length == 0) return false;
        return relativeMaxDiff_1(nums, 0, nums.length - 1) >= 0;
    }

    public int relativeMaxDiff_1(int[] nums, int start, int end) {
        if (start < 0 || end >= nums.length || start > end) return 0;//边界条件判断
        if (start == end) return nums[start];//递归终止条件
        return Math.max(nums[start] - relativeMaxDiff_1(nums, start + 1, end),
                nums[end] - relativeMaxDiff_1(nums, start, end - 1));
    }

    /***
     * 方法2:动态规划,dp[len][len]
     * 具体介绍看Leetcode877.
     */
    public boolean PredictTheWinner_DP1(int[] piles) {
        if (piles == null || piles.length == 0) return false;
        int len = piles.length;
        int[][] dp = new int[len][len];
        //初始化
        for (int i = 0; i < len; i++) dp[i][i] = piles[i];
        int end;
        for (int perLen = 2; perLen <= len; perLen++) {
            for (int start = 0; start <= len - perLen; start++) {
                end = start + perLen - 1;
                dp[start][end] = Math.max(piles[start] - dp[start + 1][end], piles[end] - dp[start][end - 1]);
            }
        }
        return dp[0][len - 1] >= 0;//大于0,获胜
    }

    /**
     * 方法3:动态规划解法,降低空间复杂度
     * 时间复杂度:O(N^2)
     * 空间复杂度:O(N);仅仅利用矩阵dp[][]的反对角线。
     */
    public boolean PredictTheWinner(int[] nums) {//最佳
        if (nums == null || nums.length == 0) return false;
        int len = nums.length;
        int[] dp = new int[len];//相当于dp[][]的反对角线
        //初始化
        for (int i = 0; i < len; i++) dp[i] = nums[i];
        int end;
        for (int perLen = 2; perLen <= len; perLen++) {
            for (int start = 0; start <= len - perLen; start++) {
                end = start + perLen - 1;
                dp[start] = Math.max(nums[start] - dp[start + 1], nums[end] - dp[start]);
            }
        }
        return dp[0] > 0;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给定一个字符串,请将字符串里的字符按照出现的频率降序排列。 示例 1: 输入: "tree" 输出: "eert" 解释: 'e'出现两次,'r'和't'都只出现一次。因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。 示例 2: 输入: "cccaaa" 输出: "cccaaa" 解释: 'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。注意"cacaca"是不正确的,因为相同的字母必须放在一起。 示例 3: 输入: "Aabb" 输出: "bbAa" 解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。注意'A'和'a'被认为是两种不同的字符。 Java代码如下: ``` import java.util.*; public class Solution { public String frequencySort(String s) { if (s == null || s.length() == 0) { return ""; } Map<Character, Integer> map = new HashMap<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); map.put(c, map.getOrDefault(c, 0) + 1); } List<Map.Entry<Character, Integer>> list = new ArrayList<>(map.entrySet()); Collections.sort(list, (o1, o2) -> o2.getValue() - o1.getValue()); StringBuilder sb = new StringBuilder(); for (Map.Entry<Character, Integer> entry : list) { char c = entry.getKey(); int count = entry.getValue(); for (int i = 0; i < count; i++) { sb.append(c); } } return sb.toString(); } } ``` 解题思路: 首先遍历字符串,使用HashMap记录每个字符出现的次数。然后将HashMap转换为List,并按照出现次数从大到小进行排序。最后遍历排序后的List,将每个字符按照出现次数依次添加到StringBuilder中,并返回StringBuilder的字符串形式。 时间复杂度:O(nlogn),其中n为字符串s的长度。遍历字符串的时间复杂度为O(n),HashMap和List的操作时间复杂度均为O(n),排序时间复杂度为O(nlogn),StringBuilder操作时间复杂度为O(n)。因此总时间复杂度为O(nlogn)。 空间复杂度:O(n),其中n为字符串s的长度。HashMap和List的空间复杂度均为O(n),StringBuilder的空间复杂度也为O(n)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值