文章目录
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. 预测赢家![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f7be2324c094f7fea858be9bb4783073.png)
看到这个题目的首次想法还是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);
}
}
}