【Java数据结构】——动态规划,分治,回溯
目录
一.动态规划
1.不同路径走法(力扣62)
2.旅行商问题
3.01背包问题
4.完全背包问题(每个物品都可以一直装)
1.完全背包
2.钢条切割
3.整数拆分问题(343)
5.最长公共子串
6.最长公共子序列
2.两个字符串的删除
3.最长递增子序列
二.分治
1.快速幂
2.平方根的整数部分
3.至少有k个字符的最长子串
三.回溯
1.全排列(以及全排列不重复)
2.组合(力扣77)
3.组合之和
1.力扣39 每个数字都可以多次使用。
2.力扣40(每个数字不能重复,且不能包含重复的组合)
3.力扣216(凑目标值)
一.动态规划
1.不同路径走法(力扣62)
public int uniquePaths2(int m, int n) {int[][] dp = new int[m][n];for (int i = 0; i < m; i++) {dp[i][0] = 1;}for (int j = 0; j < n; j++) {dp[0][j] = 1;}for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {dp[i][j] = dp[i - 1][j] + dp[i][j - 1];}}return dp[m - 1][n - 1];}
2.旅行商问题
3.01背包问题
static int select(Item[] items, int total) {int[] dp = new int[total + 1];for (Item item : items) {for (int j = total; j > 0; j--) {if (j >= item.weight) { // 装得下dp[j] = Integer.max(dp[j], item.value + dp[j - item.weight]);}}System.out.println(Arrays.toString(dp));}return dp[total];}static int select2(Item[] items, int total) {int[][] dp = new int[items.length][total + 1];Item item0 = items[0];for (int j = 0; j < total + 1; j++) {if (j >= item0.weight) { // 装得下dp[0][j] = item0.value;} else { // 装不下dp[0][j] = 0;}}print(dp);for (int i = 1; i < dp.length; i++) {Item item = items[i];for (int j = 0; j < total + 1; j++) {int x = dp[i - 1][j];if (j >= item.weight) {dp[i][j] = Integer.max(x,
// 上次剩余空间能装的最大价值 + 当前物品价值dp[i - 1][j - item.weight] + item.value);} else {dp[i][j] = x;}}print(dp);}return dp[dp.length - 1][total];}
优化为一维数组时,要从右往左处理(与完全背包问题的不同)
static int select(Item[] items, int total) {int[] dp = new int[total + 1];for (Item item : items) {for (int j = total; j > 0; j--) {if (j >= item.weight) { // 装得下dp[j] = Integer.max(dp[j], item.value + dp[j - item.weight]);}}System.out.println(Arrays.toString(dp));}return dp[total];}
4.完全背包问题(每个物品都可以一直装)
1.完全背包
private static int select3(Item[] items, int total) {//情况1:第一行int[] dp = new int[total + 1];for(Item item : items){for (int j = 0; j < total + 1; j++) {if (j >= item.weight){//装得下dp[j] = Math.max(item.value + dp[j - item.weight], dp[j]);}}}System.out.println("dp = " + Arrays.toString(dp));return dp[total];}
2.钢条切割
/* if(放得下)dp[i][j]=max(dp[i-1][j],当前物品价值+dp[i][j-物品重量]else(放不下)dp[i][j]=dp[i-1][j]0 1 2 3 4 钢条总长度=背包容量1 1 11 111 1111(1) (2) (3) (4)2 1 11 111 11112 21 21122(1) (5) (6) (10)3 1 11 111 11112 21 2113 2231(1) (5) (8) (10)4 1 11 111 11112 21 2113 22314(1) (5) (8) (10)物品重量*/static int cut(int[] values, int n) {int[][] dp = new int[values.length][n + 1];for (int i = 1; i < values.length; i++) {for (int j = 1; j < n + 1; j++) {if (j >= i) { // 放得下dp[i][j] = Integer.max(dp[i - 1][j], values[i] + dp[i][j - i]);} else { // 放不下dp[i][j] = dp[i - 1][j];}}print(dp);}return dp[values.length - 1][n];}
3.整数拆分问题(343)
public int integerBreak(int n) {int[] dp = new int[n + 1];Arrays.fill(dp, 1);dp[0] = 1;for (int i = 1; i < n; i++) {for (int j = 0; j < n + 1; j++) {if (j >= i) {dp[j] = Integer.max(dp[j], i * dp[j - i]);}}System.out.println(Arrays.toString(dp));}return dp[n];}
5.最长公共子串
static int lcs1(String a, String b) {int[][] dp = new int[b.length()][a.length()];char[] array = a.toCharArray();char[] brray = b.toCharArray();int max = 0;for (int i = 0; i < b.length(); i++) {for (int j = 0; j < a.length(); j++) {if (brray[i] == array[j]){if (i == 0 || j == 0){dp[i][j] = 1;}else {dp[i][j] = dp[i-1][j-1] + 1;max = Integer.max(dp[i][j], max);}}else {dp[i][j] = 0;}}}print(dp, a, b);return max;}
6.最长公共子序列
public int longestCommonSubsequence1(String text1, String text2) {char[] a = text1.toCharArray();char[] b = text2.toCharArray();int[][] dp = new int[a.length + 1][b.length + 1];for (int i = 1; i < a.length + 1; i++) {for (int j = 1; j < b.length + 1; j++) {if (a[i-1] == b[j-1]){dp[i][j] = dp[i-1][j-1] + 1;}else {dp[i][j] = Integer.max(dp[i-1][j], dp[i][j-1]);}}}return dp[a.length][b.length];}
2.两个字符串的删除
public int minDistance1(String text1, String text2) {char[] a = text1.toCharArray();char[] b = text2.toCharArray();int[][] dp = new int[a.length + 1][b.length + 1];for (int i = 1; i < a.length + 1; i++) {for (int j = 1; j < b.length + 1; j++) {if (a[i-1] == b[j-1]){dp[i][j] = dp[i-1][j-1] + 1;}else {dp[i][j] = Integer.max(dp[i-1][j], dp[i][j-1]);}}}return a.length + b.length - 2 * dp[a.length][b.length];}
}
3.最长递增子序列
/*1 2 3 41 3 6 4 91 13 16 14 19136 134 13916913691491349(1) (2) (3) (3) (4)4*/ public int lengthOfLIS(int[] nums) {int[] dp = new int[nums.length];Arrays.fill(dp, 1);for (int i = 1; i < nums.length; i++) {for (int j = 0; j < i; j++) {if (nums[i] > nums[j]) { // 满足了升序条件// 用之前递增子序列的最大长度 + 1 更新当前长度dp[i] = Integer.max(dp[i], dp[j] + 1);}}System.out.println(Arrays.toString(dp));}return Arrays.stream(dp).max().getAsInt();}
二.分治
1.快速幂
static double myPow1(double x, int n) {int p = n;if (p < 0){p = -p;}double v = myPowPositive1(x, p);return n < 0 ? 1/v : v;}static double myPowPositive1(double x, long n) {if (n == 0){return 1.0;}if (n == 1){return x;}double y = myPowPositive1(x, n / 2);if ((n & 1) == 0){return y * y;}else {return x * y * y;}}
2.平方根的整数部分
static int mySqrt1(int x) {int i = 1;int j = x;int r = 0;while (i <= j) {int m = (i + j) >>> 1;int mm = m * m;if (mm == x){return m;} else if (mm > x) {j = m - 1;}else {i = m + 1;r = m;}}return r;}
3.至少有k个字符的最长子串
你截一个子串,里面的重复出现的字母必须大于k,并求出最长子串的长度。
static int longestSubstring1(String s, int k) {int[] nums = new int[26];//遍历字符串,把出现次数存到数组中for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);int count = nums[c - 'a']++;}//遍历数组找出次数少于k的元素,从那个位置断开char[] chars = s.toCharArray();for (int i = 0; i < chars.length; i++) {char c = chars[i];int count = nums[c - 'a'];if (count < k && count != 0) {int j = i + 1;while(j < s.length() && nums[chars[j] - 'a'] < k) {j++;}System.out.println(s.substring(0,i) + "\t" + s.substring(j));return Integer.max(longestSubstring(s.substring(0, i), k),longestSubstring(s.substring(j), k));}}return s.length();}
三.回溯
1.全排列(以及全排列不重复)
static List<List<Integer>> permute(int[] nums) {List<List<Integer>> result = new ArrayList<>();Arrays.sort(nums);dfs(nums, new boolean[nums.length], new LinkedList<>(), result);return result;}static void dfs(int[] nums, boolean[] visited, LinkedList<Integer> stack, List<List<Integer>> result) {if (stack.size() == nums.length) {
// System.out.println(stack);result.add(new ArrayList<>(stack));return;}// 遍历 nums 数组,发现没有被使用的数字,则将其标记为使用,并加入 stackfor (int i = 0; i < nums.length; i++) {if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]){continue;}if (!visited[i]) {stack.push(nums[i]);visited[i] = true;dfs(nums, visited, stack, result);visited[i] = false;stack.pop();}}}
2.组合(力扣77)
n:1-n的数字,k:从中选k个进行组合
// 此 n 代表数字范围, 1~nstatic List<List<Integer>> combine(int n, int k) {List<List<Integer>> result = new ArrayList<>();dfs1(1, n, k, new LinkedList<>(), result);return result;}// start 起始处理数字static void dfs(int start, int n, int k,LinkedList<Integer> stack,List<List<Integer>> result) {if (stack.size() == k) {result.add(new ArrayList<>(stack));return;}for (int i = start; i <= n ; i++) {//剪枝// 还差几个数字 剩余可用数字if (k - stack.size() > n - i + 1) {continue;}stack.push(i);dfs(i + 1, n, k, stack, result);stack.pop();}}
问题:为什么全排列需要固定,组合却不用?
因为全排列如1,1,3和3,1,1是不同的排列,而在组合中则是一种,所以不需要固定。
3.组合之和
1.力扣39 每个数字都可以多次使用。
static void dfs1(int start, int[] candidates, int target, LinkedList<Integer> stack, List<List<Integer>> result) {if (target == 0){result.add(new ArrayList<>(stack));return;}for (int i = start; i < candidates.length; i++) {int candidate = candidates[i];if (target < candidate){continue;}stack.push(candidate);dfs1(i, candidates, target - candidate, stack, result);stack.pop();}}
2.力扣40(每个数字不能重复,且不能包含重复的组合)
还是之前那个visited固定的思想。
static void dfs1(int start, int[] candidates, boolean[] visited, int target, LinkedList<Integer> stack, List<List<Integer>> result) {if (target == 0){result.add(new ArrayList<>(stack));return;}for (int i = start; i < candidates.length; i++) {int candidate = candidates[i];if (target < candidate){continue;}if (i > 0 && candidates[i] == candidates[i - 1] && !visited[i - 1]){continue;}stack.push(candidate);visited[i] = true;dfs1(i + 1, candidates, visited, target - candidate, stack, result);visited[i] = false;stack.pop();}}
3.力扣216(凑目标值)
static void dfs1(int start, int target, int k, LinkedList<Integer> stack, List<List<Integer>> result) {if (target == 0 && stack.size() == k){result.add(new ArrayList<>(stack));return;}for (int i = start; i <= 9; i++) {//剪枝if (target < i){continue;}if (stack.size() == k) {continue;}stack.push(i);dfs1(i + 1, target - i, k, stack, result);stack.pop();}}