做DFS的题,想象构造树非常关键,把树构造的过程梳理清楚了,自然递归的步骤也就出来了。
17. Subsets: 点击打开链接
思路:对于[1,2,3]
[ ]
[1] 这边的1是在[ ]的基础上添加的
[1,2] [1,3] 所以倒回来到[1,3]的时候也应该知道是在[1]的基础上添加的
[1,2,3] 因为这层是在[1,2]的基础上添加的3
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
if(nums==null || nums.length==0){
return result;
}
Arrays.sort(nums);
helper(result,new ArrayList<>(),nums,0);
return result;
}
private void helper(List<List<Integer>> result,List<Integer> list,int[] nums,int start){
result.add(new ArrayList<>(list));
for(int i=start;i<nums.length;i++){
list.add(nums[i]);
helper(result,list,nums,i+1);
list.remove(list.size()-1);
}
}
}
18. Sebsets II: 点击打开链接
class Solution {
/**
* @param nums: A set of numbers.
* @return: A list of lists. All valid subsets.
*/
public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] nums) {
ArrayList<ArrayList<Integer>> results=new ArrayList<>();
ArrayList<Integer> list=new ArrayList<>();
Arrays.sort(nums);
if(nums==null){
return results;
}
if(nums.length==0){
results.add(list);
return results;
}
helper(nums,0,results,list);
return results;
}
private void helper(int[] nums, int startIndex,ArrayList<ArrayList<Integer>> results,ArrayList<Integer> list){
results.add(new ArrayList<>(list));
for(int i=startIndex;i<nums.length;i++){ //此处if语句也说明了之前数组排序的重要性
if(i!=0 && nums[i]==nums[i-1] && i>startIndex){ //首先要防止数组角标越界:i!=0
continue; //比较前后两个数是针对后面再一次出现同一个数的情况
} //如果前一个同样的数还没有放进去过,就不能放后一个同样的数;
list.add(nums[i]);
helper(nums,i+1,results,list);
list.remove(list.size()-1);
}
}
}
135.Combination Sum: 点击打开链接
1. [2]
-> [2,2]
-> [2,2,2]
-> [2,2,2,2], 判断和大于target,后面的[2,2,2,3],[2,2,2,6],[2,2,2,7]都不用再判断,break。
2. 回到i++,也就是外一圈的helper,[2,2,3]里判断,此时正好等于target,return。
判断[2,2,6],和大于target,后面的整个[2,2,7]都垮掉。
3. 再回到外一圈的helper,[2,3]里判断,以此类推。
4. 直到以[2]开头的都判断完,回到最外层helper,再判断以[3]开头的。
注意:当一个元素可以用很多次,结果又不能有重复list,就要取出原数组的重复元素
public class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result=new ArrayList<>();
List<Integer> list=new ArrayList<>();
if(candidates==null || candidates.length==0){
return result;
}
int[] nums=removeDuplicates(candidates);
helper(nums,target,0,0,result,list);
return result;
}
//递归的定义:寻找所有以list开头的满足条件的组合,放到result里
private void helper(int[] nums,int target,int startIndex,int sum,List<List<Integer>> result,List<Integer> list){
if(sum==target){ //递归的出口
result.add(new ArrayList<Integer>(list));
return;
}
for(int i=startIndex;i<nums.length;i++){ //这里的for循环执行两种功能,如果可以继续加值,里层的helper用的
if(sum+nums[i]>target){ //还有就是remove最后一个元素后,同一层的下一个组合用的
break; //一旦break,跳出整体for循环,并不是本层for循环
}
list.add(nums[i]);
helper(nums,target,i,sum+nums[i],result,list); //i的取值也很讲究:只要是元素可以重复使用,下次取的时候还是位置i
list.remove(list.size()-1);
}
}
private int[] removeDuplicates(int[] candidates){ //去重是必要的,不然result里可能会有重复小list
Arrays.sort(candidates);
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<candidates.length;i++){
if(!list.contains(candidates[i])){
list.add(candidates[i]);
}
}
int[] nums=new int[list.size()];
for(int i=0;i<list.size();i++){
nums[i]=list.get(i);
}
return nums;
}
}
public class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result=new ArrayList<>();
List<Integer> list=new ArrayList<>();
if(candidates==null || candidates.length==0){
return result;
}
int[] nums=removeDuplicates(candidates);
helper(nums,target,0,0,result,list);
return result;
}
private void helper(int[] nums,int target,int startIndex,int sum,List<List<Integer>> result,List<Integer> list){
if(sum==target){
result.add(new ArrayList<Integer>(list));
return;
}
for(int i=startIndex;i<nums.length;i++){
if(sum<target){
list.add(nums[i]);
helper(nums,target,i,sum+nums[i],result,list);
list.remove(list.size()-1);
}
}
}
private int[] removeDuplicates(int[] nums){
Arrays.sort(nums);
int index=0;
for(int i=0;i<nums.length;i++){
if(nums[i]!=nums[index]){
index++;
nums[index]=nums[i];
}
}
int[] result=new int[index+1];
for(int i=0;i<result.length;i++){
result[i]=nums[i];
}
return result;
}
}
153. Combination Sum II : 点击打开链接
注意:当一个元素只可以用一次,原数组里又有重复元素,结果又不能有重复list,就要判断后面的元素是不是先于前面的元素被使用
public class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result=new ArrayList<>();
List<Integer> list=new ArrayList<>();
if(candidates==null || candidates.length==0){
return result;
}
Arrays.sort(candidates);
helper(candidates,target,0,0,result,list);
return result;
}
private void helper(int[] nums,int target,int startIndex,int sum,List<List<Integer>> result,List<Integer> list){
if(sum==target){
result.add(new ArrayList<Integer>(list));
return;
}
for(int i=startIndex;i<nums.length;i++){
if(sum<target){
if(i!=0 && nums[i]==nums[i-1] && i!=startIndex){ //因为i-1等于startIndex
continue; //nums[i-1]没有被拿过,就不能拿nums[i]
}
list.add(nums[i]);
helper(nums,target,i+1,sum+nums[i],result,list);
list.remove(list.size()-1);
}
}
}
}
136. Palindrome Partitioning: 点击打开链接
public class Solution {
/**
* @param s: A string
* @return: A list of lists of string
*/
public List<List<String>> partition(String s) {
List<List<String>> result=new ArrayList<>();
List<String> list=new ArrayList<>();
if(s==null || s.length()==0){
return result;
}
helper(s,0,result,list);
return result;
}
private void helper(String s,int startIndex,List<List<String>> result,List<String> list){
if(startIndex==s.length()){
result.add(new ArrayList<>(list));
return;
}
for(int i=startIndex;i<s.length();i++){
String sub=s.substring(startIndex,i+1);
if(!isPalindrome(sub)){
continue; //并不是最大层for循环整体垮掉,只是终止本层for循环
}
list.add(sub);
helper(s,i+1,result,list);
list.remove(list.size()-1);
}
}
private boolean isPalindrome(String s){
for(int i=0, j=s.length()-1; i<j ; i++, j--){
if(s.charAt(i)!=s.charAt(j)){
return false;
}
}
return true;
}
}
267. Palindrome Partitioning II
Given a string s
, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form.
Example 1:
Input: "aabb"
Output: ["abba", "baab"]
Example 2:
Input: "abc"
Output: []
class Solution {
private Map<Character, Integer> map = new HashMap<>();
private List<String> results = new ArrayList<>();
private String middleChar = "";
public List<String> generatePalindromes(String s) {
if(s == null || s.length() == 0)
{
return results;
}
boolean bFormed = canBeFormed(s);
if(bFormed)
{
helper(results, s, middleChar);
}
return results;
}
private void helper(List<String> results, String s, String tempS)
{
if(tempS.length() == s.length())
{
results.add(tempS);
return;
}
for(Character c: map.keySet())
{
int countForVal = map.get(c);
if(countForVal >= 2)
{
map.put(c, countForVal - 2);
helper(results, s, c+tempS+c);
map.put(c, countForVal);
}
}
}
// 不要受I的思路影响, 判断是否可以形成Palindrome非常关键
// 如果一个字符串里的字符是奇数的个数大于1,就完全形不成,如果只有1个字符的个数是奇数,把那个的一个放在最中间即可
private boolean canBeFormed(String s)
{
char[] c = s.toCharArray();
for(int i=0; i<c.length; i++)
{
map.put(c[i], map.getOrDefault(c[i],0)+1);
}
for(Character k: map.keySet()){
int count = map.get(k);
if(count%2 != 0){
if(middleChar != "")
{
return false;
}
middleChar = k+"";
}
}
return true;
}
}
15. Permutations: 点击打开链接
public class Solution {
public List<List<Integer>> permute(int[] nums) { //recursion方法一
List<List<Integer>> result=new ArrayList<>();
if(nums==null || nums.length==0){
return result;
}
int[] visited=new int[nums.length];
for(int i=0;i<nums.length;i++){
visited[i]=0;
}
helper(result,new ArrayList<>(),nums,visited);
return result;
}
private void helper(List<List<Integer>> result,List<Integer> list,int[] nums,int[] visited){
if(list.size()==nums.length){
result.add(new ArrayList<>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(visited[i]==1){
continue;
}
list.add(nums[i]);
visited[i]=1;
helper(result,list,nums,visited);
list.remove(list.size()-1);
visited[i]=0;
}
}
}
class Solution { //recursion方法二
/**
* @param nums: A list of integers.
* @return: A list of permutations.
*/
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
List<Integer> list=new ArrayList<>();
if(nums==null){
return result;
}
if(nums.length==0){
result.add(list);
return result;
}
helper(result,list,nums);
return result;
}
private void helper(List<List<Integer>> result,
List<Integer> list,
int[] nums){
if(list.size()==nums.length){
result.add(new ArrayList<>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(list.contains(nums[i])){
continue;
}
list.add(nums[i]);
helper(result,list,nums);
list.remove(list.size()-1);
}
}
}
16. Permuations II : 点击打开链接
class Solution {
/**
* @param nums: A list of integers.
* @return: A list of permutations.
*/
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
List<Integer> list=new ArrayList<>();
if(nums==null){
return result;
}
if(nums.length==0){
result.add(list);
return result;
}
Arrays.sort(nums);
int[] visited=new int[nums.length];
for(int i=0;i<visited.length;i++){
visited[i]=0;
}
helper(result,list,nums,visited);
return result;
}
private void helper(List<List<Integer>> result,
List<Integer> list,
int[] nums,
int[] visited){
if(list.size()==nums.length){
result.add(new ArrayList<>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(visited[i]==1 || (i!=0 && nums[i]==nums[i-1] && visited[i-1]==0)){
continue; //如果有相同元素的情况下,如果前面的元素还没有使用的时候,就不能让后面这个元素使用
} //也就是轮到这个元素的时候,它已经被放进去了,或者它前面的元素还没有被放进去
list.add(nums[i]);
visited[i]=1;
helper(result,list,nums,visited);
list.remove(list.size()-1);
visited[i]=0;
}
}
}
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
[ "((()))", "(()())", "(())()", "()(())", "()()()" ]
class Solution {
public List<String> generateParenthesis(int n) {
List<String> results = new ArrayList<>();
helper(results, n, 0, 0, new StringBuilder());
return results;
}
private void helper(List<String> results, int n, int left, int right, StringBuilder tempS)
{
int length = tempS.length();
if(left == n && right == n)
{
results.add(tempS.toString());
return;
}
if(left < right || left > n || right > n) // 如果右半括号多于左半括号, EX.)()(
{ // 或者任意面半括号数量多于给定个数,都是不符合条件的情况
return;
}
helper(results, n, left+1, right, tempS.append("("));
tempS.setLength(length);
helper(results, n, left, right+1, tempS.append(")"));
tempS.setLength(length);
}
}
33. N-Queens: 点击打开链接
1.同的一行不能存在两个皇后,但是又正好是N个皇后又要放在N行上,这就意味着每一行都有且仅有一个皇后,于是我们可以按照这样的方式来枚举这道题目的所有方案:依次枚举每一行皇后的位置,在这个枚举过程中确保不会在每一列、每一条斜线上出现两个皇后,对于行里面的每一个元素,都要遍历一次这个元素对应的一整列。
2.以[1,3,0,2]的ArrayList为例,也就是arrayList.get(0)=1, arrayList.get(1)=3, arrayList.get(2)=0, arrayList.get(3)=2
。 | Q | 。 | 。 |
。 | 。 | 。 | Q |
Q | 。 | 。 | 。 |
。 | 。 | Q | 。 |
3.明确造成斜线攻击的条件:同一个斜线上的坐标之差相等,反方向上是同一个斜线上坐标之和相等。
class Solution {
/**
* Get all distinct N-Queen solutions
* @param n: The number of queens
* @return: All distinct solutions
* For example, A string '...Q' shows a queen on forth position
*/
ArrayList<ArrayList<String>> solveNQueens(int n) {
ArrayList<ArrayList<String>> result=new ArrayList<>();
ArrayList<Integer> cols=new ArrayList<>();
if(n<=0){
return result;
}
search(result,cols,n);
return result;
}
private void search(ArrayList<ArrayList<String>> result,
ArrayList<Integer> cols,
int n){
if(cols.size()==n){
result.add(drawChessBoard(cols));
return;
}
for(int colIndex=0;colIndex<n;colIndex++){
if(!isValid(cols,colIndex)){
continue;
}
cols.add(colIndex);
search(result,cols,n);
cols.remove(cols.size()-1);
}
}
private boolean isValid (ArrayList<Integer> cols,int column){ //判断有效落子位置
int row=cols.size();
for(int rowIndex=0;rowIndex<cols.size();rowIndex++){ //行里面的每一个元素,都要遍历一次这个元素对应的一整列
if(cols.get(rowIndex)==column){ //造成列攻击
return false;
}
if(rowIndex+cols.get(rowIndex)==row+column){ //造成斜线攻击
return false;
}
if(rowIndex-cols.get(rowIndex)==row-column){ //造成斜线攻击
return false;
}
}
return true;
}
private ArrayList<String> drawChessBoard(ArrayList<Integer> cols) { //每一个solution画成棋盘
ArrayList<String> solution = new ArrayList<>();
for (int i = 0; i < cols.size(); i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < cols.size(); j++) {
if(j == cols.get(i)){
sb.append('Q');
}else{
sb.append('.');
}
}
solution.add(sb.toString());
}
return solution; //最后以这样的形式表示棋盘[.Q.., ...Q, Q..., ..Q.], 以[1,3,0,2]为例
}
}
34.N-Queens II : 点击打开链接
N-QueensII相对于I其实是更简单了,不让打印满足条件的棋盘,而是输出满足条件的棋盘个数。
class Solution {
/**
* Calculate the total number of distinct N-Queen solutions.
* @param n: The number of queens.
* @return: The total number of distinct solutions.
*/
public int sum;
public int totalNQueens(int n) {
sum=0;
search(new ArrayList<Integer>(), n);
return sum;
}
private void search(ArrayList<Integer> solution,int n){ //注意这边不用初始化sum=0;
if(solution.size()==n){
sum++;
return;
}
for(int colIndex=0; colIndex<n; colIndex++){
if(!isValid(solution,colIndex)){
continue;
}
solution.add(colIndex);
search(solution,n);
solution.remove(solution.size()-1);
}
}
private boolean isValid(ArrayList<Integer> solution,int column){
int row=solution.size();
for(int rowIndex=0;rowIndex<solution.size();rowIndex++){
if(solution.get(rowIndex)==column){
return false;
}
if(rowIndex+solution.get(rowIndex)==row+column){
return false;
}
if(rowIndex-solution.get(rowIndex)==row-column){
return false;
}
}
return true;
}
};
12.Min Stack:点击打开链接
public class MinStack {
Stack<Integer> stack=new Stack<>();
Stack<Integer> minStack=new Stack<>();
public MinStack() {
// do initialize if necessary
}
public void push(int number) {
stack.push(number);
if(minStack.isEmpty() || minStack.peek()>=number){ //如果栈是空的,可以直接放,如果栈里有就要比较与加入number的大小
minStack.push(number); //只有原来peek的比number的值大至少等于number,才可以加入number
} //也就是说,如果要加入的一直比minStack.peek()的大,就一直不加入minStack里
}
public int pop() {
int x=stack.pop();
if(minStack.peek()==x){ //如果当初加入的时候加入的比minStack原有的大,则minStack不加入
minStack.pop(); //这时pop的时候minStack也不pop
}
return x;
}
public int min() {
return minStack.peek();
}
}
575.Expression Expand: 点击打开链接
public class Solution {
/**
* @param s an expression includes numbers, letters and brackets
* @return a string
*/
public String expressionExpand(String s) {
Stack<Object> stack=new Stack<>();
int num=0;
for(char c : s.toCharArray()){ //c只有4种:数字, '[', ']'或者字符串
if(Character.isDigit(c)){ //如果是数字,就拿到数字
num=num*10+c-'0';
}else if(c=='['){ //如果是'[',就把数字入栈
stack.push(Integer.valueOf(num)); //数字入栈之后要归零,后面的数字还要在for循环里出现
num=0;
}else if(c==']'){ //如果是']',就要拿出[]所包含的字符串,并让之前相应的数字出栈
String curString=popStack(stack);
Integer count=(Integer)stack.pop();
for(int i=0;i<count;i++){ //有几个字符串,就以for循环的形式入栈几次
stack.push(curString); //解决了形如 3[abc]这样的问题
}
}else{ //如果是字符串,就直接入栈
stack.push(String.valueOf(c));
}
}
return popStack(stack); //最后最大括号的整体也要当做字符串
}
private String popStack(Stack<Object> stack){ //将stack里的字符串出栈,导入暂时栈temp,然后添加到sb里转化为String
Stack<String> tempStack=new Stack<>();
String str=null;
while(!stack.isEmpty() && stack.peek() instanceof String){
tempStack.push((String) stack.pop());
}
StringBuilder sb=new StringBuilder();
while(!tempStack.isEmpty()){
sb.append(tempStack.pop());
}
return sb.toString();
}
}
433.Numbers of Islands: 点击打开链接
对于每一个
grid[i][j]点进行遍历
, 如果是小岛,即grid[i][j] == true
,则result+1,并对四个方向进行DFS查找(查找的时候要先判断有效点,也就是坐标加减后还在给定矩阵中的点),并将所有属于那坐岛屿的点标记为非岛屿。这样遍历过的地方全部会变成非岛屿,而岛屿的数量已被记录。(标记的意义在于:不会对同一块已经计数过的小岛反复计数)。
public class Solution {
/**
* @param grid a boolean 2D matrix
* @return an integer
*/
public int numIslands(boolean[][] grid) { //写法一
if(grid==null || grid.length==0){
return 0;
}
int result=0;
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
if(grid[i][j]==true){
result++;
dfs(grid,i,j);
}
}
}
return result;
}
private void dfs(boolean[][] grid,int x,int y){ //找grid[i][j]四周为岛屿的点
int[] dx={0,0,-1,1}; //如果有就把它们标记为false,表示和grid[i][j]连接
int[] dy={-1,1,0,0};
if(isValid(grid,x,y) && grid[x][y]==true){
grid[x][y]=false;
for(int k=0;k<4;k++){
dfs(grid,x+dx[k],y+dy[k]);
}
}
}
private boolean isValid(boolean[][] grid,int x,int y){
if(x<0 || x>=grid.length){
return false;
}
if(y<0 || y>=grid[0].length){
return false;
}
return true;
}
}
public class Solution {
/**
* @param grid a boolean 2D matrix
* @return an integer
*/
public int numIslands(boolean[][] grid) { //写法二,思路是一样的
if(grid==null || grid.length==0){
return 0;
}
int result=0;
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
if(grid[i][j]==true){
result++;
dfs(grid,i,j);
}
}
}
return result;
}
private void dfs(boolean[][] grid, int i, int j){
if(i<0 || i>=grid.length || j<0 || j>=grid[0].length){
return;
}
if(grid[i][j]==true){
grid[i][j]=false;
dfs(grid,i+1,j);
dfs(grid,i-1,j);
dfs(grid,i,j+1);
dfs(grid,i,j-1);
}
}
}