目录
[案例1】
【题目描述】
【思路解析1】
【思路解析2】
【代码实现】
【案例2】
【题目描述】
【思路解析】
【代码实现】
【案例3】
【题目描述】
【思路解析】
【代码实现】
【案例4】
【题目描述】今日头条2018面试题 第四题
【输入描述】
【思路解析】
【代码实现】
【案例5】
【题目描述】
【思路解析】
【代码实现】
【案例6】
【题目描述】
【思路解析】
【代码描述】
【案例7】
【题目描述】
【思路解析】
【代码实现】
【案例8】
【题目描述】
【思路解析】
【代码实现】
【改为动态规划】
【案例9】
【题目描述】
【思路解析】
【代码实现】
【案例10】
【题目描述】
【思路解析】
【代码实现】
【案例11】
【题目描述】
【思路解析】
【代码实现】
[案例1】
【题目描述】
给定一个非负整数n,代表二叉树的节点个数。返回能形成多少种不同的二叉树结构。
【思路解析1】
如果n小于0,返回0,如果n为1,可以形成一个空二叉树结构,如果n为1也可以形成一种二叉树结构,如果n为2,可以形成两种二叉树结构。对于其余普遍情况来说,总的情况为,(1)左子树没有节点,右子树有n-1个节点。(2)左子树一个节点,右子树n-2个节点.......(n)左子树n-1个节点,右子树无节点。这所有情况的总和。当一个子树节点超过二时,有可以把它作为主树来使用递归。
【思路解析2】
通过暴力递归改为动态规划。
【代码实现】
/**
* @ProjectName: study3
* @FileName: Ex1
* @author:HWJ
* @Data: 2023/7/10 8:10
*/
public class Ex1 {
public static void main(String[] args) {
System.out.println(dpWays(3));
}
public static int ways(int n) {
return process(n);
}
public static int process(int n) {
if (n < 0) {
return 0;
}
if (n == 0) {
return 1;
}
if (n == 1) {
return 1;
}
if (n == 2) { // 这都是baseCase条件
return 2;
}
int res = 0;
for (int l = 0; l <= n - 1; l++) { // 按照策略算总和
int left = process(l); // 进行暴力递归
int right = process(n - l - 1);
res += left * right;
}
return res;
}
public static int dpWays(int n) {
if (n < 0) {
return 0;
}
if (n < 2){
return 1;
}
int[] dp = new int[n + 1];
dp[0] = 1; // 根据baseCase初始化
for (int i = 1; i <= n; i++) { // 这里通过i来求不同节点的情况数。
for (int j = 0; j < i; j++) {
dp[i] += dp[j] * dp[i - j - 1]; // 通过递归改为动态规划
}
}
return dp[n];
}
}
【案例2】
【题目描述】
【思路解析】
如何找到需要添加几个括号,首先我们要明白如何判断它是否是一个完整的括号字符串。
(1)先分析如何判断它是否是一个完整的括号字符串,使用一个变量count,当遍历到左括号时++,当遍历到右括号--,当在某个时刻时,如果count<0则不是一个完整字符串。如果遍历到最后,count==0,则是一个完整字符串。
(2)判断它需要添加多少个括号,使用变量count和ans,当遍历到左括号时count++,当遍历到右括号(1)如果count==0,此时需要添加左括号,ans++,(2)如果count>0,此时count--。遍历到最后,count>0,表示需要添加右括号,所以返回ans+count。
【代码实现】
/**
* @ProjectName: study3
* @FileName: Ex2
* @author:HWJ
* @Data: 2023/7/10 9:19
*/
public class Ex2 {
public static void main(String[] args) {
}
public static boolean isComplete(String s) {
char[] chars = s.toCharArray();
int count = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '(') {
count++;
}else {
count--;
}
if (count < 0) { // 如果出现任意时候,右括号比左括号,这时一定不能构成完整括号字符串
return false;
}
}
return count == 0;
// 结束后count必须等于0
}
public static int needRight(String s) {
char[] chars = s.toCharArray();
int count = 0;
int ans = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '(') {
count++;
}else {
if (count == 0) {
ans++;
} else {
count--;
}
}
}
return ans + count;
}
}
【案例3】
【题目描述】
【思路解析】
因为要求去重,所有可以HashSet结构来做,然后再遍历每个元素num,然后查询num+k是否存在,这样就可以找到每个去重数字对了。
【代码实现】
import java.util.HashSet;
/**
* @ProjectName: study3
* @FileName: Ex3
* @author:HWJ
* @Data: 2023/7/10 10:04
*/
public class Ex3 {
public static void main(String[] args) {
}
public static int k_num(int[] arr, int k) {
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < arr.length; i++) {
set.add(arr[i]);
}
int count = 0;
for (Integer i : set) {
if (set.contains(i + k)) {
count++;
}
}
return count;
}
}
【案例4】
【题目描述】今日头条2018面试题 第四题
给一个包含n个整数元素的集合a,一个包含m个整数元素的集合b。
定义magic操作为,从一个集合中取出一个元素,放到另一个集合里,切操作过后每个集合的平均值都大于操作前。
注意一下两点:
不可以把一个集合的元素取空,这样就没有平均值了
值为x的元素从集合b取出放入集合a,但集合a中已经有值为x的元素,则a的平均值不变(因为集合元素不会重复),b的平均值可能会改变(因为x被取出了)
问最多可以进行多少次magic操作?
【输入描述】
第一行为两个整数n,m
第二行n
个整数,表示集合a中的元素
第三行m
个整数,表示集合b中的元素
对于100%的数据,,集合a
中元素互不相同,集合b
中的元素互不相同。
【思路解析】
对于集合a的平均值假设为avg_a,集合b的平均值假设为avg_b
(1)avg_a==avg_b,无法找到有效值取出
a. 取出a集合中大于avg_a的值,会使a的平均值下降,不满足要求
b. 取出a集合中等于avg_a的值,集合a和b平均值不会变化,不满足要求。
c. 取出a集合中小于avg_a的值,会使b的平均值下降,不满足要求。
(2)avg_a<avg_b,无法找到有效值取出
a. 取出a集合中大于avg_a的值,会使a的平均值下降,不满足要求。
b. 取出a集合中等于avg_a的值,会使b的平均值下降,不满足要求。
c. 取出a集合中小于avg_a的值,会使b的平均值下降,不满足要求。
(3)avg_a>avg_b
a. 取出a集合中大于avg_a的值,会使a的平均值下降,不满足要求。
b. 取出a集合中等于avg_a的值,会使b的平均值上升,但a集合的平均值不会变化,不满足要求。
c. 取出a集合中小于avg_a的值并且大于avg_b的值,会使b的平均值上升,会使avg_a的平均值上升,满足要求。
(c.1) 取出a集合中小于avg_a的值并且大于avg_b的值,这样的值可能有多个,我们应选择这里面中最小的值(并且满足b集合不存在这个值),这样可以使avg_a变大幅度尽可能大,可以avg_b变大的幅度尽可能小。这样差值越大,则选择的值可能会进一步变多。
【代码实现】
import java.util.Arrays;
import java.util.HashSet;
/**
* @ProjectName: study3
* @FileName: Ex4
* @author:HWJ
* @Data: 2023/7/10 10:46
*/
public class Ex4 {
public static void main(String[] args) {
}
public static int times(int[] a, int[] b) {
int sumA = 0;
for (int i = 0; i < a.length; i++) {
sumA += a[i];
}
int sumB = 0;
for (int i = 0; i < b.length; i++) {
sumB += b[i];
}
if (avg(sumA, a.length) == avg(sumB, b.length)) {
return 0;
}
int[] arrMore;
int[] arrLess;
int moreSize;
int lessSize;
int sumMore;
int sumLess;
if (avg(sumA, a.length) > avg(sumB, b.length)) { // 对数组进行预处理
arrMore = a;
arrLess = b;
sumMore = sumA;
sumLess = sumB;
moreSize = a.length;
lessSize = b.length;
} else {
arrMore = b;
arrLess = a;
sumMore = sumB;
sumLess = sumA;
moreSize = b.length;
lessSize = a.length;
}
HashSet<Integer> lessSet = new HashSet<>(); // 将较小平均值的数组创建一个集合,方便查询后面要加入的值是否存在
for (int i = 0; i < lessSize; i++) {
lessSet.add(arrLess[i]);
}
Arrays.sort(arrMore); // 对较大平均值的数组进行排序,保证拿出的有效数一定是较小的那个
int ops = 0; // 操作数
for (int i = 0; i < arrMore.length; i++) {
int cur = arrMore[i];
if (cur < avg(sumMore, moreSize) &&
cur > avg(sumLess, lessSize) &&
!lessSet.contains(cur)) { // 按顺序依次拿出较小值
sumMore -= cur; // 拿出后对数据进行处理
sumLess += cur;
moreSize--;
lessSize++;
lessSet.add(cur);
ops++;
}
}
return ops;
}
public static double avg(int sum, int len) {
return (sum * 1.0) / len;
}
}
【案例5】
【题目描述】
【思路解析】
它给出的是一个合法的括号序列,所以我们并不需要验证它是否是完整的括号字符串序列。使用一个遍历count,遇到左括号count++,遇到右括号count--,其中count的最大值就是这个括号字符串序列的深度。
【代码实现】
/**
* @ProjectName: study3
* @FileName: Ex5
* @author:HWJ
* @Data: 2023/7/10 11:18
*/
public class Ex5 {
public static void main(String[] args) {
System.out.println(depth("(()(()))"));
}
public static int depth(String s){
if (s == null){
return 0;
}
char[] chars = s.toCharArray();
int count = 0;
int max =Integer.MIN_VALUE;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '('){
count++;
}else {
count--;
}
max = Math.max(count, max);
}
return max;
}
}
【案例6】
【题目描述】
给你一个字符串其中只包含‘(’和‘)’,找出其中最长的有效括号字符串,并返回这个字符串的长度。
【思路解析】
计算以当前字符作为结尾时,最长有效括号字符串的长度为多少,然后统计其中的最大值。当当前字符为左括号时,以次字符为结尾不可能有左子符。如果当前字符为右括号,它就需要找到前面是否有左括号与其配对。怎么找到前面和它配对的字符在那个位置上,如果当前字符位置为i,我们可以知道i-1位置上有效字符串,则配对位置应该为i-dp[i-1]-1,如果为左括号则配对成功,否则配对失败。
配对成功后,怎么计算当前位置的有效长度:dp[i] = dp[i-1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
【代码描述】
/**
* @ProjectName: study3
* @FileName: Ex6
* @author:HWJ
* @Data: 2023/7/10 11:25
*/
public class Ex6 {
public static void main(String[] args) {
}
public static int maxLength(String s) {
if (s == null || s.equals("")) {
return 0;
}
char[] chars = s.toCharArray();
int[] dp = new int[chars.length];
int pre = 0;
int res = 0;
for (int i = 1; i < chars.length; i++) {
if (chars[i] == ')') {
pre = i - dp[i - 1] - 1;
if (pre >= 0 && chars[pre] == '(') {
dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
}
【案例7】
【题目描述】
【思路解析】
构建一个辅助栈,其中按降序的方式进行排序,当原始栈不为空时,弹出元素,如果能按照降序的方式加入辅助栈就直接加入,否则就弹出辅助栈的元素重新加入原始栈,知道满足降序时将次元素加入辅助栈。
【代码实现】
import java.util.Stack;
/**
* @ProjectName: study3
* @FileName: Ex7
* @author:HWJ
* @Data: 2023/7/10 12:55
*/
public class Ex7 {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.add(2);
stack.add(9);
stack.add(7);
stack.add(6);
stack.add(4);
stack.add(5);
stack.add(3);
sort(stack);
while (!stack.isEmpty()){
System.out.println(stack.pop());
}
}
public static void sort(Stack<Integer> stack){
Stack<Integer> help = new Stack<>();
while (!stack.isEmpty()){
int cur = stack.pop();
if (help.isEmpty()){
help.add(cur);
}else {
while (!help.isEmpty() && cur > help.peek()){
stack.add(help.pop());
}
help.add(cur);
}
}
while (!help.isEmpty()){
stack.add(help.pop());
}
}
}
【案例8】
【题目描述】
【思路解析】
【代码实现】
/**
* @ProjectName: study3
* @FileName: NumToStr
* @author:HWJ
* @Data: 2023/6/12 14:31
*/
public class NumToStr {
public static void main(String[] args) {
}
public static int translate(String str){
char[] charArray = str.toCharArray();
return process(charArray, 0);
}
public static int process(char[] str, int i){
if ( i == str.length){ // 如果已经把所有的都决定完了,返回1
return 1;
}
if (str[i] == '0'){ // 如果第i位为0,没法转换,返回0
return 0;
}
if (str[i] == '1'){
int res = process(str, i+1); // i 自己作为单独的部分,后续有多少种方法,
if (i+1 < str.length){
res += process(str, i+2);// (i 和 i+1) 自己作为单独的部分,后续有多少种方法
}
return res;
}
if (str[i] == '2'){
int res = process(str, i+1); // i 自己作为单独的部分,后续有多少种方法,
if (i+1 < str.length && str[i+1] >= '0' && str[i+1] <= '6'){ // (i 和 i+1) 自己作为单独的部分,后续有多少种方法,
res += process(str, i+2); // 明确要求其不能超过26
}
return res;
}
return process(str, i+1);
}
}
【改为动态规划】
/**
* @ProjectName: study3
* @FileName: Ex8
* @author:HWJ
* @Data: 2023/7/10 14:32
*/
public class Ex8 {
public static void main(String[] args) {
}
public static int dpWays(String s) {
if (s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
int[] dp = new int[N + 1];
dp[N] = 1;
dp[N - 1] = str[N - 1] == 0 ? 0 : 1;
for (int i = N - 2; i >= 0; i--) {
if (str[i] == '0') {
dp[i] = 0;
} else {
dp[i] = dp[i + 1];
int t = dp[i + 1] + (dp[i] - '0') * 10;
if (t <= 26) {
dp[i] += dp[i + 2];
}
}
}
return dp[0];
}
}
【案例9】
【题目描述】
【思路解析】
这里使用递归。很简单,详情看代码。
【代码实现】
说明:Node节点的代码很简单,这里懒得给出了。
/**
* @ProjectName: study3
* @FileName: Ex9
* @author:HWJ
* @Data: 2023/7/10 14:43
*/
public class Ex9 {
public static void main(String[] args) {
}
public static int getMax(Node head){
if (head == null){
return 0;
}
return process(head);
}
public static int process(Node node){
int left = node.value;
int right = node.value;
if (node.right != null){
right += process(node.right);
}
if (node.left != null){
left += process(node.left);
}
return Math.max(left, right);
}
}
【案例10】
【题目描述】
【思路解析】
因为它每行和每列都是有序的,所以遍历从矩阵右上角开始。如果当前位置大于目标值就往左滑动一下,,如果当前位置小于目标值就往下滑动。直到越界了,还没有找到目标值就返回false。
【代码实现】
/**
* @ProjectName: study3
* @FileName: Ex10
* @author:HWJ
* @Data: 2023/7/10 15:12
*/
public class Ex10 {
public static void main(String[] args) {
int[][] matrix = {{1, 5, 9, 10}, {2, 6, 11, 13}, {7, 9, 15, 17}};
System.out.println(search(matrix, 7));
}
public static boolean search(int[][] matrix, int aim) {
int N = matrix.length;
int M = matrix[0].length;
int i = 0;
int j = M - 1;
while (i >= 0 && i < N && j >= 0 && j < M) {
if (matrix[i][j] > aim) {
j--;
} else if (matrix[i][j] < aim) {
i++;
} else {
return true;
}
}
return false;
}
}
【案例11】
【题目描述】
给出一个二维矩阵,每行都只有1和0,并且规定左边全是0,右边全是1,返回含1最多的行号。
如 00011111 或者 0000000 或者 11111111
【思路解析】
从右上角开始,滑动到第一行的第一个1的位置,并记录当前max1(表示目前已知最多1的数量),然后将第一行的行号加入到list中。然后往下滑动,如果此位置是0,就继续往下滑动,如果此位置是1,就参看是否能往左滑动,如果能,则清空list,并重新记录max1和将此行行号加入到list中,如果不能,则把此行行号加入到list中。
【代码实现】
import java.util.LinkedList;
/**
* @ProjectName: study3
* @FileName: Ex11
* @author:HWJ
* @Data: 2023/7/10 15:32
*/
public class Ex11 {
public static void main(String[] args) {
int[][] matrix = {{0,0,0,0,1,1,1},{0,0,0,1,1,1,1},{0,0,0,0,0,0,1},{0,0,0,1,1,1,1},{1,1,1,1,1,1,1}};
LinkedList<Integer> list = search(matrix);
for (int i :list) {
System.out.println(i);
}
}
public static LinkedList<Integer> search(int[][] matrix) {
int N = matrix.length;
int M = matrix[0].length;
int i = 0;
int j = M - 1;
LinkedList<Integer> list = new LinkedList<>();
while (i >= 0 && i < N && j >= 0 && j < M) {
if (j > 0 && matrix[i][j-1] == 1) {
if (i != 0) { // 如果向下滑动后能左滑,就清空列表
list.clear();
}
j--;
} else{
if (i == 0){ //如果此时不能左滑了,此时为第一行,则直接加入,
list.add(i+1);
}else {
if(matrix[i][j] == 1){ // 如果不是第一行,当前位置是1,就加入
list.add(i+1);
}
}
i++;
}
}
return list;
}
}