贪心
121. 买卖股票的最佳时机
解法一、从前往后遍历
class Solution {
public int maxProfit(int[] prices) {
int profit = 0;
int min = Integer.MAX_VALUE;
for(int i = 0; i < prices.length; i++){
min = Math.min(prices[i], min);
profit = Math.max(profit, prices[i] - min);
}
return profit;
}
}
解法二、从后往前遍历
class Solution {
public int maxProfit(int[] prices) {
int res = 0,n = prices.length,max = prices[n-1];
if(n==1)return 0;
for(int i = n-2;i >= 0;i--){//从n-2开始
res = Math.max(res,max-prices[i]);
max = Math.max(max,prices[i]);
}
return res;
}
}
55. 跳跃游戏
解法一、从前往后
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length,max = 0;
for(int i = 0;i<n;i++){
if(max < i)return false;
max = Math.max(max,i+nums[i]);
}
return true;
}
}
解法二、从后往前
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length,step = 1;
for(int i = n - 2;i >= 0;i--){
if(nums[i] < step){
step++;
}else{
step = 1;
}
}
return step == 1;
}
}
45. 跳跃游戏 II
解法一、
每次修桥都走到头(即maxNow)
class Solution {
public int jump(int[] nums) {
int n = nums.length,max = 0,maxNow = 0,res = 0;
for(int i = 0;i<n-1;i++){
if(max < i + nums[i])max = i+nums[i];
if(i == maxNow && max > maxNow){
res++;
maxNow = max;
}
}
return res;
}
}
763. 划分字母区间
解法一、贪心
①记录尾标
②第二个循环更新尾标,只要i到尾标=可以结束 记载进答案
import java.util.Arrays;
class Solution {
public List<Integer> partitionLabels(String s) {
char[] c = s.toCharArray();
int n = s.length();
int[] last = new int[26];
for(int i = 0;i < n;i++){
last[c[i] - 'a'] = i;
}
List<Integer> res = new LinkedList<>();
int start = 0,end = 0;
for(int i = 0;i<n;i++){
end = Math.max(end,last[c[i] - 'a']);
if(i == end){
res.add(end - start +1);
start = i+1;
}
}
return res;
}
}
回溯
46. 全排列
解法一、回溯
class Solution {
private void dfs(int i,int[] nums, List<List<Integer>> res,boolean[] onPath,List<Integer> path){
if(i == nums.length){
res.add(new ArrayList<>(path));
return;
}
for(int j = 0; j < nums.length;j++){
if(!onPath[j]){
//如果不在里面,那么
path.set(i,nums[j]);
onPath[j] = true;
dfs(i+1,nums,res,onPath,path);
onPath[j] = false;
}
}
}
public List<List<Integer>> permute(int[] nums) {
List<Integer> path = Arrays.asList(new Integer[nums.length]);
boolean[] onPath = new boolean[nums.length];
List<List<Integer>> res = new LinkedList<>();
dfs(0,nums,res,onPath,path);
return res;
}
}
78. 子集
解法一、选或不选
class Solution {
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new LinkedList<>();
private void dfs(int i,int[] nums){
if(i == nums.length){
res.add(new ArrayList<>(path));
return;
}
dfs(i+1,nums);
path.add(nums[i]);
dfs(i+1,nums);
path.remove(path.size()-1);
}
public List<List<Integer>> subsets(int[] nums) {
dfs(0,nums);
return res;
}
}
解法二、选哪个
class Solution {
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new LinkedList<>();
private void dfs(int i,int[] nums){
res.add(new ArrayList<>(path));
for(int j = i;j<nums.length;j++){
path.add(nums[j]);
dfs(j+1,nums);
path.remove(path.size()-1);
}
}
public List<List<Integer>> subsets(int[] nums) {
dfs(0,nums);
return res;
}
}
17. 电话号码的字母组合
解法一、回溯
务必注意处理空的情况
可以考虑把StringBuffer换成一个固定长度为n的char数组
class Solution {
private String[] a = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
List<String> res = new ArrayList<>();
StringBuffer tmp = new StringBuffer();
public List<String> letterCombinations(String digits) {
if(digits.equals(""))return res;
char[] c = digits.toCharArray();
dfs(0,c);
return res;
}
private void dfs(int i,char[] c){
if(i == c.length){
res.add(tmp.toString());
return;
}
String s = a[c[i] - '0'];
int l = s.length();
for(int j = 0;j < l;j++){
tmp.append(s.charAt(j));
dfs(i+1,c);
tmp.delete(tmp.length()-1,tmp.length());
}
}
}
39. 组合总和
解法一、回溯
class Solution {
private List<List<Integer>> res = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
dfs(0,candidates,target);
return res;
}
private void dfs(int i,int[] candidates, int target){
if(target == 0){
res.add(new ArrayList<>(path));
return;
}
int l = candidates.length;
if(i == l)return;
for(int j = i;j < l;j++){
if(target>=candidates[j]){
path.add(candidates[j]);
dfs(j,candidates,target-candidates[j]);
path.remove(path.size()-1);
}
}
}
}
22. 括号生成
解法一、回溯
class Solution {
private List<String> res = new ArrayList<>();
private char[] tmp;
public List<String> generateParenthesis(int n) {
tmp = new char[2*n];
dfs(n,0,0,tmp);
return res;
}
private void dfs(int n,int len,int left,char[] tmp){
if(len == 2*n){
res.add(new String(tmp));
return;
}
if(left < n){//可以放左
tmp[len] = '(';
dfs(n,len+1,left+1,tmp);
}
if(left * 2 > len){//可以放右
tmp[len] = ')';
dfs(n,len+1,left,tmp);
}
}
}
79. 单词搜索
解法一、回溯
需要注意在return false之前要恢复v
优化一:在bfs中可以将board改值,表示已遍历。
优化二:统计一下字母数量,board<word则肯定没有。
优化三:如果首字母远大于尾字母数量,倒悬搜索也可以。
class Solution {
private int[][] indirection = {{1,0},{-1,0},{0,1},{0,-1}};
private boolean[][] isVisited;
public boolean exist(char[][] board, String word) {
int m = board.length,n = board[0].length;
isVisited = new boolean[m][n];
isVisited[0][0] = true;
Deque<int[]> d = new LinkedList<>();
d.push(new int[]{0,0});
while(!d.isEmpty()){
int[] tmp = d.pop();
isVisited[tmp[0]][tmp[1]] = true;
if(board[tmp[0]][tmp[1]] == word.charAt(0)){
boolean[][] v = new boolean[m][n];
if(bfs(board,word,1,tmp[0],tmp[1],v))return true;
}
if(tmp[0] + 1 < m && !isVisited[tmp[0]+1][tmp[1]])d.push(new int[] { tmp[0]+1,tmp[1]});
if(tmp[1] + 1 < n&& !isVisited[tmp[0]][tmp[1]+1])d.push(new int[] { tmp[0],tmp[1]+1});
}
return false;
}
private boolean isValid(int m,int n,int x, int y){
return x >= 0 && y >= 0 && x < m && y < n;
}
public boolean bfs(char[][]board,String word,int i,int x,int y,boolean[][] v){
if(i == word.length())return true;
v[x][y] = true;
int m = board.length,n = board[0].length;
for(int[] dir : indirection){
int x1 = x + dir[0];
int y1 = y + dir[1];
if(isValid(m,n,x1,y1) && !v[x1][y1] && board[x1][y1] == word.charAt(i)) {
if(bfs(board,word,i+1,x1,y1,v))return true;
}
}
v[x][y] = false;
return false;
}
}
131. 分割回文串
解法一、回溯
res.add的时候忘了复制了
class Solution {
private List<List<String>> res = new ArrayList<>();
private List<String> tmp = new ArrayList<>();
public List<List<String>> partition(String s) {
dfs(s,0);
return res;
}
private void dfs(String s,int i){
if(i == s.length()) {
res.add(new ArrayList<>(tmp));
return;
}
int len = s.length();
for(int j = i;j < len;j++){
if(isValid(s,i,j)){
tmp.add(s.substring(i,j+1));
dfs(s,j+1);
tmp.remove(tmp.size()-1);
}
}
}
private boolean isValid(String s,int x,int y){
while(x < y){
if(s.charAt(x++) != s.charAt(y--))return false;
}
return true;
}
}
51. N 皇后
解法一、回溯
三个bool数组判断合理性是精华
import java.util.Arrays;
class Solution {
private List<List<String>> res = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
int[] queen = new int[n];
boolean[] col = new boolean[n];
boolean[] diag1 = new boolean[2 * n - 1];
boolean[] diag2 = new boolean[2 * n - 1];
dfs(queen,col,diag1,diag2,0);
return res;
}
private void dfs(int[] queen,boolean[] col,boolean[] diag1,boolean[] diag2,int i){
int n = queen.length;
if(i == n){
List<String> board = new ArrayList<>(n);
char[] c = new char[i];
Arrays.fill(c,'.');
for(int j = 0;j < i;j++){
c[queen[j]] = 'Q';
board.add(new String(c));
c[queen[j]] = '.';
}
res.add(new ArrayList<>(board));
return;
}
for(int j = 0;j < n;j++){
if(!col[j] && !diag1[i+j] && !diag2[j - i + n - 1]){
queen[i] = j;
col[j] = true;
diag1[i + j] = true;//0,0 1,2 2,1
diag2[j - i + n - 1] = true;
dfs(queen,col,diag1,diag2,i+1);
col[j] = diag1[i+j] = diag2[j - i + n - 1] = false;
}
}
}
}