文章目录
一、递归/回溯
1.没有重复项数字的全排列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param num int整型一维数组
* @return int整型ArrayList<ArrayList<>>
*/
//交换函数,交换i和j下标的元素
public void swap(ArrayList<Integer> list,int i,int j){
int tmp = list.get(i);
list.set(i,list.get(j)) ;
list.set(j,tmp);
}
public void permuteHelper(ArrayList<ArrayList<Integer>> result,ArrayList<Integer> list,int index){
//遍历到最后一个元素了是一个排列就将当前list添加到结果集当中
if(index == list.size() - 1){
result.add(list);
}else{
for(int i = index;i < list.size();i++){
//交换
swap(list,index,i);
// 继续寻找
permuteHelper(result,list,index + 1);
//回溯
swap(list,index,i);
}
}
}
public ArrayList<ArrayList<Integer>> permute (int[] num) {
// write code here
Arrays.sort(num);
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0;i < num.length;i++){
list.add(num[i]);
}
permuteHelper(result,list,0);
return result;
}
}
2.有重复项数字的全排列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param num int整型一维数组
* @return int整型ArrayList<ArrayList<>>
*/
// dfs:深度优先遍历算法
public void dfs(ArrayList<ArrayList<Integer>> result,ArrayList<Integer> list,boolean[] flag,int[] num){
// 当前list的大小与数组大小相同,说明是一个排序结果
if(list.size() == num.length){
result.add(new ArrayList<>(list));
}
for(int i = 0;i < num.length;i++){
// 如果当前位置的标记为true,说明已经被使用过了
if(flag[i]){
continue;
}
// 如果当前位置的元素的值和前一个相同说明前一个重复的元素已经使用过了就跳过
if(i > 0 && num[i] == num[i - 1] && !flag[i - 1]){
continue;
}
// 添加当前元素
list.add(num[i]);
// 标记当前元素
flag[i] = true;
dfs(result,list,flag,num);
// 回溯
list.remove(list.size() - 1);
flag[i] = false;
}
}
public ArrayList<ArrayList<Integer>> permuteUnique (int[] num) {
// write code here
Arrays.sort(num);
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();
//用来标记当前元素是否已经被遍历过(相同元素去重)
boolean[] flag = new boolean[num.length];
// 将指定的布尔值分配给指定的布尔数组的每个元素
Arrays.fill(flag,false);
dfs(result,list,flag,num);
return result;
}
}
3.岛屿的数量
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 判断岛屿数量
* @param grid char字符型二维数组
* @return int整型
*/
public void dfs(char[][] grid, int i, int j) {
int n = grid.length;
int m = grid[0].length;
// 将当前位置的元素置为‘0’
grid[i][j] = '0';
// 向四个方向寻找
if (i - 1 >= 0 && grid[i - 1][j] == '1') {
dfs(grid, i - 1, j);
}
if (i + 1 < n && grid[i + 1][j] == '1') {
dfs(grid, i + 1, j);
}
if (j - 1 >= 0 && grid[i][j - 1] == '1') {
dfs(grid, i, j - 1);
}
if (j + 1 < m && grid[i][j + 1] == '1') {
dfs(grid, i, j + 1);
}
}
public int solve (char[][] grid) {
// write code here
int n = grid.length;
if (n == 0) {
return 0;
}
int m = grid[0].length;
int count = 0;//岛屿的数量
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '1') {
count++;
dfs(grid, i, j);
}
}
}
return count;
}
}
4.字符串的排序
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @return string字符串ArrayList
*/
public void dfs(ArrayList<String> list,char[] ch,boolean[] flag,StringBuffer sb){
if(sb.length() == ch.length){
list.add(new String(sb));
}
for(int i = 0;i < ch.length;i++){
if(flag[i]){
continue;
}
if(i > 0 && ch[i] == ch[i - 1] && !flag[i - 1]){
continue;
}
sb.append(ch[i]);
flag[i] = true;
dfs(list,ch,flag,sb);
sb.deleteCharAt(sb.length() - 1);
flag[i] = false;
}
}
public ArrayList<String> Permutation (String str) {
// write code here
ArrayList<String> list = new ArrayList<>();
char[] ch = str.toCharArray();
Arrays.sort(ch);
boolean[] flag = new boolean[ch.length];
Arrays.fill(flag,false);
StringBuffer sb = new StringBuffer();
dfs(list,ch,flag,sb);
return list;
}
}
5.括号生成
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @return string字符串ArrayList
*/
public void dfs(int left, int right, ArrayList<String> list, String str,int n) {
if (left == n && right == n) {
list.add(str);
}
if (left < n) {
dfs(left + 1, right, list, str + '(', n);
}
if (right < n && left > right) {
dfs(left, right + 1,list, str + ')', n);
}
}
public ArrayList<String> generateParenthesis (int n) {
// write code here
ArrayList<String> list = new ArrayList<>();
String str = "";
dfs(0, 0, list, str, n);
return list;
}
}
6.矩阵最长递增路径
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 递增路径的最大长度
* @param matrix int整型二维数组 描述矩阵的每个数
* @return int整型
*/
// 四个方向的值
public int[][] point = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 深度遍历函数
public int dfs(int[][] matrix,int[][] dp,int i,int j){
// 说明当前位置已经走过了
if(dp[i][j] != 0){
return dp[i][j];
}
// 说明当前位置符合就加上当前位置
dp[i][j]++;
for(int k = 0;k < 4;k++){
int ni = i + point[k][0];
int nj = j + point[k][1];
if(ni >= 0 && ni < matrix.length && nj >= 0 && nj < matrix[0].length && matrix[i][j] < matrix[ni][nj]){
dp[i][j] = Math.max(dp[i][j],dfs(matrix,dp,ni,nj) + 1);
}
}
return dp[i][j];
}
public int solve (int[][] matrix) {
// write code here
if(matrix == null || matrix.length == 0){
return 0;
}
int n = matrix.length;
int m = matrix[0].length;
int[][] dp = new int[n + 1][m + 1];
int sum = 0;//最终的矩阵的最长递增路径长度
// 遍历当前矩阵的每一个位置
for(int i = 0;i < n;i++){
for(int j = 0;j < m;j++){
sum = Math.max(sum,dfs(matrix,dp,i,j));
}
}
return sum;
}
}
二、动态规划
1.斐波那契数列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @return int整型
*/
public int Fibonacci (int n) {
// write code here
if(n == 1 || n == 2){
return 1;
}
int first = 1;
int second = 1;
while(n > 2){
int third = first + second;
first = second;
second = third;
n--;
}
return second;
}
}
2.跳台阶
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param number int整型
* @return int整型
*/
public int jumpFloor (int number) {
// write code here
if(number == 1 || number == 2){
return number;
}
int first = 1;
int second = 2;
while(number > 2){
int third = first + second;
first = second;
second = third;
number--;
}
return second;
}
}
3.最小花费爬楼梯
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param cost int整型一维数组
* @return int整型
*/
public int minCostClimbingStairs (int[] cost) {
// write code here
int[] dp = new int[cost.length + 1];
for(int i = 2;i <= cost.length;i++){
dp[i] = Math.min(dp[i - 1] + cost[i -1],dp[i - 2] + cost[i - 2]);
}
return dp[cost.length];
}
}
4.最长公共子序列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* longest common subsequence
* @param s1 string字符串 the string
* @param s2 string字符串 the string
* @return string字符串
*/
public String ans(int len1,int len2,int[][] direction,String s1,String s2){
String res = "";
if(len1 == 0 || len2 == 0){
return res;
}
// 根据方向,往前递归,然后添加本级字符
if(direction[len1][len2] == 1){
res += ans(len1 - 1,len2 - 1,direction,s1,s2);
res += s1.charAt(len1 - 1);
}else if(direction[len1][len2] == 2){
res += ans(len1 - 1,len2,direction,s1,s2);
}else if(direction[len1][len2] == 3){
res += ans(len1,len2 - 1,direction,s1,s2);
}
return res;
}
public String LCS (String s1, String s2) {
// write code here
if(s1.length() == 0 || s2.length() == 0){
return new String("-1");
}
// dp[i][j]表示第一个字符串到第i位,第二个字符串到第j位为止的最长公共子序列长度
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
// 动态规划数组相加的方向
int[][] direction = new int[s1.length() + 1][s2.length() + 1];
for(int i = 1;i <= s1.length();i++){
for(int j = 1;j <= s2.length();j++){
// 遇到两个字符相等
if(s1.charAt(i - 1) == s2.charAt(j - 1)){
dp[i][j] = dp[i - 1][j - 1] + 1;
// 来自左上方
direction[i][j] = 1;
}else{
// 左边的选择更大,即第一个字符串后退一位
if(dp[i - 1][j] > dp[i][j - 1]){
dp[i][j] = dp[i - 1][j];
// 来自左方
direction[i][j] = 2;
}else{
// 右边的选择更大,即第二个字符串后退一位
dp[i][j] = dp[i][j - 1];
// 来自上方
direction[i][j] = 3;
}
}
}
}
String res = ans(s1.length(),s2.length(),direction,s1,s2);
if(res.isEmpty()){
return new String("-1");
}else{
return res;
}
}
}
5.最长公共子串
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* longest common substring
* @param str1 string字符串 the string
* @param str2 string字符串 the string
* @return string字符串
*/
public String LCS (String str1, String str2) {
// write code here
int maxLength = 0;
// 记录最长公共子串最后一个元素在字符串str1中的位置
int lastIndex = 0;
int[][] dp = new int[str1.length() + 1][str2.length() + 1];
for(int i = 0;i < str1.length();i++){
for(int j = 0;j < str2.length();j++){
if(str1.charAt(i) == str2.charAt(j)){
dp[i + 1][j + 1] = dp[i][j] + 1;
//如果遇到了更长的子串,要更新,记录最长子串的长度以及最长子串最后一个元素的位置
if(maxLength < dp[i + 1][j + 1]){
maxLength = dp[i + 1][j + 1];
lastIndex = i;
}
}else{
dp[i + 1][j + 1] = 0;
}
}
}
return str1.substring(lastIndex - maxLength + 1,lastIndex + 1);
}
}
6.不同路径的数目
递归:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param m int整型
* @param n int整型
* @return int整型
*/
public int uniquePaths (int m, int n) {
if(m == 1 || n == 1){
return 1;
}
return uniquePaths(m-1,n) + uniquePaths(m,n - 1);
}
}
非递归:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param m int整型
* @param n int整型
* @return int整型
*/
public int uniquePaths (int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for(int i = 1;i <= m;i++){
for(int j = 1;j <= n;j++){
if(i == 1){
dp[i][j] = 1;
continue;
}
if(j == 1){
dp[i][j] = 1;
continue;
}
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m][n];
}
}
7.把数字翻译成字符串
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 解码
* @param nums string字符串 数字串
* @return int整型
*/
public int solve (String nums) {
// write code here
if(nums.equals("0")){
return 0;
}
// 处理10、20的特殊情况
if(nums.equals("10") || nums.equals("20")){
return 1;
}
int[] dp = new int[nums.length() + 1];
// 当0前面不是0或1是无法编译
for(int i = 1;i < nums.length();i++){
if(nums.charAt(i) == '0'){
if(nums.charAt(i - 1) != '1' && nums.charAt(i - 1) != '2'){
return 0;
}
}
}
// 初始化数组的值,全部初始化为1
Arrays.fill(dp,1);
for(int i = 2;i <= nums.length();i++){
// 在11-19,21-26之间的情况
if((nums.charAt(i - 2) == '1' && nums.charAt(i - 1) != '0') ||
(nums.charAt(i - 2) == '2' && nums.charAt(i - 1) > '0' && nums.charAt(i - 1) < '7')){
// 当nums[i-1] = 0; nums[i-2] 等于1或者2时,dp[i] = dp[i-2]
if(i < nums.length() && nums.charAt(i) == '0'){
dp[i] = dp[i - 2];
}else{
dp[i] = dp[i - 1] + dp[i - 2];
}
}else{
dp[i] = dp[i - 1];
}
}
return dp[nums.length()];
}
}
8.兑换零钱
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 最少货币数
* @param arr int整型一维数组 the array
* @param aim int整型 the target
* @return int整型
*/
public int minMoney (int[] arr, int aim) {
// write code here
if(aim < 1){
return 0;
}
// dp[i]表示要凑出i元钱需要的最小的货币数
int[] dp = new int[aim + 1];
// 将dp数组全部初始化为最大值aim+1
Arrays.fill(dp,aim + 1);
dp[0] = 0;
// 遍历从1元到aim元
for(int i = 1;i <= aim;i++){
for(int j = 0;j < arr.length;j++){
// 枚举每种面值的货币都可能组成的情况,取每次的最小值
// 转移方程:dp[i] = min(dp[i],dp[i - arr[j] + 1])
if(arr[j] <= i){
dp[i] = Math.min(dp[i],dp[i - arr[j]] + 1);
}
}
}
// 如果最后的dp[aim]的值大于aim说明无解(说明还是初始化的值,并没有更新)
return dp[aim] > aim ? -1 : dp[aim];
}
}
9.最长上升子序列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 给定数组的最长严格上升子序列的长度。
* @param arr int整型一维数组 给定的数组
* @return int整型
*/
public int LIS (int[] arr) {
// write code here
if(arr.length == 1){
return 1;
}
int[] dp = new int[arr.length + 1];
Arrays.fill(dp,1);
int ret = 0;
for(int i = 1;i < arr.length;i++){
for(int j = 0;j < i;j++){
if(arr[i] > arr[j]){
dp[i] = Math.max(dp[i],dp[j] + 1);
}
}
ret = Math.max(dp[i],ret);
}
return ret;
}
}
10.连续子数组的最大和
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型一维数组
* @return int整型
*/
public int FindGreatestSumOfSubArray (int[] array) {
// write code here
if(array == null || array.length == 0){
return 0;
}
int[] dp = new int[array.length];
dp[0] = array[0];
int max = dp[0];
for(int i = 1;i < array.length;i++){
dp[i] = Math.max(dp[i - 1] + array[i],array[i]);
if(max < dp[i]){
max = dp[i];
}
}
return max;
}
}
11.最长回文子串
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param A string字符串
* @return int整型
*/
public int func(int i,int j,String A){
// 以每个点为中心
while(j < A.length() && i >= 0 && A.charAt(i) == A.charAt(j)){
i--;
j++;
}
// 返回长度
return j - i - 1;
}
public int getLongestPalindrome (String A) {
// write code here
if(A == null || A.length() == 0){
return 0;
}
int maxLen = 1;
// 以每个点为中心统计长度
for(int i = 0;i < A.length() - 1;i++){
// 以奇数和偶数点为中心得到最大长度
maxLen = Math.max(maxLen,Math.max(func(i,i,A),func(i,i + 1, A)));
}
return maxLen;
}
}
12.将数字字符串转化为ip地址
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param s string字符串
* @return string字符串ArrayList
*/
public ArrayList<String> restoreIpAddresses (String s) {
// write code here
ArrayList<String> list = new ArrayList<>();
if(s == null || s.length() == 0){
return list;
}
int n = s.length();
for(int i = 1;i < 4;i++){
for(int j = i + 1;j < i + 4;j++){
for(int k = j + 1;k < j + 4 && k < n;k++){
if(n - k >= 4){
continue;
}
String a = s.substring(0,i);
String b = s.substring(i,j);
String c = s.substring(j,k);
String d = s.substring(k,n);
if(Integer.parseInt(a) > 255 || Integer.parseInt(b) > 255 || Integer.parseInt(c) > 255 || Integer.parseInt(d) > 255 ){
continue;
}
if((a.length() > 1 && a.charAt(0) == '0') || (b.length() > 1 && b.charAt(0) == '0') || (c.length() > 1 && c.charAt(0) == '0') || (d.length() > 1 && d.charAt(0) == '0')){
continue;
}
String str = a + "." + b + "." + c + "." + d;
list.add(str);
}
}
}
return list;
}
}
13.打家劫舍(一)
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int rob (int[] nums) {
// write code here
// dp[i]表示偷钱的数目
int[] dp = new int[nums.length + 1];
dp[1] = nums[0];
for(int i = 2;i <= nums.length;i++){
dp[i] = Math.max(dp[i - 1],nums[i - 1] + dp[i - 2]);
}
return dp[nums.length];
}
}
14.打家劫舍(二)
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int rob (int[] nums) {
// write code here
int[] dp = new int[nums.length + 1];
// 偷第一家,则最后一家不能偷
dp[1] = nums[0];
for(int i = 2;i < nums.length;i++){
dp[i] = Math.max(dp[i - 1],dp[i - 2] + nums[i - 1]);
}
int ret = dp[nums.length - 1];
Arrays.fill(dp,0);
// 不偷第一家(dp[1] = 0),则最后一家能偷
for(int i = 2;i <= nums.length;i++){
dp[i] = Math.max(dp[i - 1],dp[i - 2] + nums[i - 1]);
}
return Math.max(ret,dp[nums.length]);
}
}
15.买股票的最好时期(一)
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param prices int整型一维数组
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int n = prices.length;
// dp[i][0]表示某一天不持股到该天为止的最大收益,dp[i][1]表示某天持股,到该天为止的最大收益
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1;i < n;i++){
dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1],-prices[i]);
}
return dp[n - 1][0];
}
}
16.买股票的最好时期(二)
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 计算最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int n = prices.length;
// dp[i][0]表示某一天不持股到该天为止的最大收益,dp[i][1]表示某天持股,到该天为止的最大收益
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1;i < n;i++){
// 如果当天不持股,有可能是前面的若干天中卖掉了或是还没买,因此到此为止的总收益和前一天相同,也有可能是当天卖掉股票
dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
// 如果当天持股,可能是前几天买入的还没卖,因此收益与前一天相同,也有可能是当天买入,减去买入的花费,同样是选取最大值
dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
}
return dp[n - 1][0];
}
}
17.买卖股票的最好时机(三)
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 两次交易所能获得的最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int n = prices.length;
int[][] dp = new int[n][5];
Arrays.fill(dp[0],-10000);
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1;i < n;i++){
dp[i][0] = dp[i - 1][0];
// 如果当天状态为1,可能是之前买过了或者当天才第一次买入
dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
// 如果当天状态是2,那必须是在1的状态下(已经买入了一次)当天卖出第一次,或者早在之前就卖出只是还没买入第二次
dp[i][2] = Math.max(dp[i - 1][2],dp[i - 1][1] + prices[i]);
// 如果当天状态是3,那必须是在2的状态下(已经卖出了第一次)当天买入了第二次,或者早在之前就买入了第二次,只是还没卖出
dp[i][3] = Math.max(dp[i - 1][3],dp[i - 1][2] - prices[i]);
// 如果当天状态是3,那必须是在2的状态下(已经卖出了第一次)当天买入了第二次,或者早在之前就买入了第二次,只是还没卖出
dp[i][4] = Math.max(dp[i - 1][4],dp[i - 1][3] + prices[i]);
}
return Math.max(0,Math.max(dp[n - 1][2],dp[n - 1][4]));
}
}