【每周N题】8.30-9.12 堆的使用,回溯问题,最大最小问题

577. 反转字符

在这里插入图片描述

主要还是考察Java中关于字符串处理的api的使用。

  • StringBuffer sb = new StringBuffer();
  • ans.append(new StringBuffer(s.substring(pre, i)).reverse().toString());
  • ans.deleteCharAt(ans.length()-1);

主要熟悉这些api的方法。

class Solution {
    public String reverseWords(String s) {
        s += ' ';
        StringBuffer ans = new StringBuffer();
        int pre = 0;
        for (int i = 0; i < s.length(); i++){
            if (s.charAt(i) == ' '){
                ans.append(new StringBuffer(s.substring(pre, i)).reverse().toString());
                ans.append(' ');
                pre = i+1;
            }
        }
        ans.deleteCharAt(ans.length()-1);
        return ans.toString();
    }
}

486. 预测赢家在这里插入图片描述

看到这个题目的首次想法还是dp,但是如何做好这次dp呢?

  • 方法一

首先我的想法是这是一道典型的区间dp问题,dp[left][right]表示在这个区间内先手用户可以得到的最大的总得分。因此转换方程就是dp[i][j] = max(min(dp[i+1][j-1], dp[i+2][j])+num[i], min(dp[i][j-2], dp[i-1][j-1])+num[j])

初始状态为,dp[i][i+1] = num[i]。循环的方法还是最外侧是区间长度,中间是起点,并且计算得到终点。因为这里只能取两边的数值,因此不存在第三次循环。

  • 方法二

因为我们只关心能否获胜,因此我们其实只要平衡两者的分差就行。定义dp[i][j]为区间中先手方可以的分差。dp[i][j] = max(nums[i]-dp[i+1][j], nums[j]-dp[i][j-1])

 当前区间内可以的最大得分。
class Solution {
    public boolean PredictTheWinner(int[] nums) {
        int n = nums.length;
        int sum = 0;
        for (int i = 0; i<n; i++){
            sum += nums[i];
        }
        int[][] dp = new int[n][n];

        for (int len = 1; len<=n; len++){
            for (int i = 0 ; i+len-1<n ;i++){
                int j = i+len-1;
                if (len == 1) {
                    dp[i][i] = nums[i];
                }
                else if (len == 2){
                    dp[i][j] = Math.max(nums[j], nums[i]);
                }
                else {
                  dp[i][j] = Math.max(Math.min(dp[i+1][j-1], dp[i+2][j])+nums[i], Math.min(dp[i][j-2], dp[i+1][j-1])+nums[j]);
                } 
            }
        }
        int ans = dp[0][n-1];
        int res = sum-ans;
        return ans>=res;
    }
}

/// 第二个方法,考虑当前拿的人在这个区间先手可以胜的差距。
class Solution {
    public boolean PredictTheWinner(int[] nums) {
        int length = nums.length;
        int[][] dp = new int[length][length];
        for (int i = 0; i < length; i++) {
            dp[i][i] = nums[i];
        }
        for (int i = length - 2; i >= 0; i--) {
            for (int j = i + 1; j < length; j++) {
                dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
            }
        }
        return dp[0][length - 1] >= 0;
    }
}

51. N皇后

在这里插入图片描述

两个问题,如何衡量两个斜列;递归需要注意,从行遍历的话,需要保证每行都有,否则可以直接return。

class Solution {
    int[] r; // 列
    int[] a;
    int[] b;
    List<List<String>> ans = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        r = new int[n];
        a = new int[2*n];
        b = new int[2*n];
        int[] cur = new int[n];
        backtracking(n,n,cur,0);
        return ans;
    }

    public void backtracking(int n, int res, int[] cur, int line){
        if(res == 0) {
            List<String> newcur = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                char[] row = new char[n];
                Arrays.fill(row, '.');
                row[cur[i]] = 'Q';
                newcur.add(new String(row));
            }
            ans.add(newcur);
            return;
        }
        for (int j = 0; j <n;j++){
            if (r[j] == 0 && a[line+j] == 0 && b[n-line+j] == 0){
                r[j] = 1; 
                a[line+j] = 1;
                b[n-line+j] = 1;
                cur[line] = j;
                backtracking(n, res-1,cur, line+1);
                r[j] = 0; 
                a[line+j] = 0;
                b[n-line+j] = 0;
            }
        }
    }
}

257. 二叉树的所有路径

在这里插入图片描述

题目的坑在于,我们需要使用StringBuffer类型的结构,但是我们要注意每次新建一个,而不是传入当前的,因为这个是可变的,传入的是地址。

class Solution {
    List<String> ans;
    public List<String> binaryTreePaths(TreeNode root) {
        ans = new ArrayList<>();
        if (root == null) return ans;
        StringBuffer cur =  new StringBuffer().append(root.val);
        if (root.left == null && root.right == null) ans.add(cur.toString());
        else {
            dfs(root.left,new StringBuffer(cur));
            dfs(root.right,new StringBuffer(cur));
        }
        return ans;
    }
    public void dfs(TreeNode root, StringBuffer cur){
        if (root == null){
            return ;
        }
        else{
            cur.append("->");
            cur.append(root.val); // 更完整应该是下面这个语句
            //cur.append(Integer.toString(root.val));
            if (root.left == null && root.right == null) ans.add(cur.toString());
            else{
                 这里一定需要注意,我们要新建一个StringBuffer的实例,否则会传入同样地址的。
                dfs(root.left,new StringBuffer(cur));  
                dfs(root.right,new StringBuffer(cur));
            }
        }

    }
}

60. 第K个排列

在这里插入图片描述

这里我们需要在删除数据后,重新编号数组的序号,这个对于py比较麻烦,但是在java中有Linked List实现的数组,因此需要有这个思想使用。

class Solution {
    public int[] C;
    public String getPermutation(int n, int k) {
        // 注意:相当于在 n 个数字的全排列中找到下标为 k - 1 的那个数,因此 k 先减 1
        k--;
        C = new int [n+1];
        helper(n);
        List<Integer> nums = new LinkedList<>();
        for (int i = 1; i<=n;i++) nums.add(i);
        StringBuffer ans = new StringBuffer();
        for (int i = n-1; i>=0 ; i--){
            int index = k/C[i];
            ans.append(nums.remove(index));
            k -= C[i]*index;
        }
        return ans.toString();
    }
    public void helper(int n){
        C[0] = 1;
        for (int i = 1; i<=n;i++)  C[i] = C[i-1]*i;
    }
}

347. 前 K 个高频元素【堆】

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        // 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
        HashMap<Integer,Integer> map = new HashMap();
        for(int num : nums){
            if (map.containsKey(num)) {
               map.put(num, map.get(num) + 1);
             } else {
                map.put(num, 1);
             }
        }
        // 遍历map,用最小堆保存频率最大的k个元素
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return map.get(a) - map.get(b);
            }
        });
        // 树中比较大小的核心是不同数值出现的次数。
        for (Integer key : map.keySet()) {
            if (pq.size() < k) {
                pq.add(key);
            } else if (map.get(key) > map.get(pq.peek())) {
                pq.remove(); /// 删除最顶端的,也就是目前出现次数最小的。
                pq.add(key);
            }
        }
        // 取出最小堆中的元素
        int[] res = new int[k];
        int i = 0;
        while (!pq.isEmpty()) {
            int cur = pq.remove();
            res[i] = cur;
            i++;
        }
        return res;
    }
}


40.组合总和

在这里插入图片描述

经典的回溯问题,因为限制了不能使用重复的方法,这里注意一下判断条件。

class Solution {
    List<List<Integer>> ans;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        ans = new ArrayList<>();
        backtracking(candidates, target, 0, 0, new ArrayList<Integer>());
        return ans;
    }
    private void backtracking(int[] candidates, int target, int cur, int begin, List<Integer> res){
        int n = candidates.length;
        if (cur == target){
            ans.add(res);
            return;
        }
        for (int i = begin;i<n; i++){
            if(cur+candidates[i]>target) continue;
            // 关键一步
            if (i>begin && candidates[i-1] == candidates[i]) continue;
            res.add(candidates[i]);
            backtracking(candidates, target, cur+candidates[i], i+1, new ArrayList<Integer>(res));
            res.remove(res.size()-1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值