以下为本人在刷题时的笔记,思路及要点仅作为参考,不作为标准的答案。不足之处,欢迎在评论区批评指正~
boolean数组默认true → 0和1不是 → 从i=2开始,对i的倍数从i倍开始,置为false
public static boolean isPrimer(int n) {
boolean[] countPrime = new boolean[n+1];
Arrays.fill(countPrime, true);//1. 先默认都是质数
countPrime[0] = false; countPrime[1] = false;//2. 0-1不是质数
//3. 如果x是质数,那么2x, 3x, 4x...不是质数
/*后面的数都默认是质数,从2,3,5,7...开始依次将它们的倍数排除,总能将所有数字遍历过一遍*/
for(int i = 2; i <= n; i++) {
if(countPrime[i] && i*i <= n) {
for(int j = i*i; j <= n; j +=i) {//i的倍数所以每次j+i
countPrime[j] = false;
}
}
}
return countPrime[n];
}//isPrimer
概念:公因数只有1
判断方法:1)两个质数互质;2)1与任何数互质;3)a的所有质因子与b的所有质因子互质。
public static boolean isHuzhi(int a, int b) {//是否互质
if(isPrimer(a) && isPrimer(b)) {//两个质数互质
return true;
}else if(a == 1 && b == 1) {//1与任何数互质
return true;
}else{//如果没有相同的质因子,则互质
Set<Integer> set1 = new HashSet<Integer>(), set2 = new HashSet<Integer>();
set1 = getZhiList(a); set2 = getZhiList(b);
int count = 0;//相同质因子的个数
for(Integer num : set1) {
if(set2.contains(num)) count++;
}
if(count == 0) return true;
}//if-else
return false;
}//isHuzhi
public static Set<Integer> getZhiList(int n){//Set内的元素不重复
/*因为只需要判断是否有相同的质因子,某个质因子的数量无关,所以可以用Set*/
Set<Integer> set = new HashSet<Integer>();
int i = 2;
while(i <= n) {
if(isPrimer(i) && (n % i == 0)) {//是质数,且能整除,那就是质因子
n = n / i;
set.add(i);
if(n == 1) break;//已经除到1了
i = 2;//i又重新从2开始算
}else {
i++;
}
}//while
return set;
}//getZhiList
如果只需要在一个方向上操作数组,且每次只需要判断第一个数,则用Queue。但这题不是,所以不能用Queue,数组判断边界很重要。
认真读题。
01 00 为 00 01
02 00 为 00 02
03 00 为 00 03
走台阶的dp[]是一维的
注意检查边界。
分析好题目即可。
去掉首位空格。
str = str.trim();
String[] str_arr = sc.nextLine().split(",");
/*个人理解:
2. stream(str_arr):把str_arr数组放入stream流中;
3. mapToInt():把String类型的数字映射成int;
4. Integer::parseInt==还不是很理解,“意会”:Integer.parseInt(str_arr中的每个数字)
5. toArray():转成数组类型。
*/
int[] nums = Arrays.stream(str_arr).mapToInt(Integer::parseInt).toArray();
List<Integer> list;
list = Arrays.stream(sc.nextLine().split(" ")).map(Integer::parseInt).collect(Collectors.toList());
遍历map的value
for(Integer num : map.values()) max = Math.max(max, num);
二叉树的遍历,需要记录路径
用数组存的时候,下标从1开始
数组排序后计算即可。认真读题
不是回溯,是循环
1)区别
String在重新赋值后,回开辟一个新的空间来存储新的字符串;
StringBuilder不会重新分配内存,而是在原来内存空间的基础上操作;二者在拼接时语法也有差异。
所以,涉及字符串拼接的,最好用StringBuilder
2)转换
s = sb + "";//String 类型很包容,什么加一下""就能变成String
s += "abc";//String对象可以用加号+
sb.append("abc");//StringBuilder对象要用append()方法
头脑清醒就好。
List<Map.Entry<Character, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Character, Integer>>(){
@Override
public int compare(Entry<Character, Integer> o1, Entry<Character, Integer> o2) {
// TODO Auto-generated method stub
return o2.getValue() - o1.getValue();//降序
}
});
读懂题目是王道
使用str.subsring()需注意
str.replace(temp, "");//不改变str的值
str = str.replace(temp, "");//需要赋值才能改变
理解好步长和步数
如何标记已经使用过的调度是重点。
对于List
需要同时记录次序、身高、体重,所以需要int[n][3]的数字;身高升序,身高相同则按体重升序,即先按第二列排序,再按第三列排序。
int[][] hw = new int[n][3];
Arrays.sort(hw, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {//对每个int[1][3]维的数组,
if(o1[1] == o2[1]) //如果第1维身高相等
return o1[2] - o2[2];//体重升序排列
else
return o1[1] - o2[1];//否则就按照身高递增排列
}
});
Lambda表达式不太好理解,还是用常规方法把
取十进制的最低位,就mod 10,负数的最低位也为负数,即-21 % 10 = -1。
匹配前缀
try {
for(String word : set) {
if(word.length() >= pre.length()) {
if(word.substring(0, pre.length()).equals(pre)) {
System.out.print(word + " ");
}
}
}//for-set
}catch(Exception e) {
System.out.println(pre);
}
可以先想好关键代码怎么写,然后再决定以什么数据类型接收输入。
想不到取巧的方法那就暴力破解
好好审题即可。
审题。
操作两个数组时,要分析清楚是两层for循环还是双指针移动
参赛团队的最低能力值为N。
流水线作业,作业已经安排好了,不是实时的,所以还比较好算。找到数学模型。
动态规划 + 认真分析。
需要动点脑子的,真正笔试的时候,可以,先写别的,再回来写这个。
有些题是逻辑复杂,有些题是数据处理繁琐
//1. 回溯函数的参数列表:由于操作的就是1-n的数,所以不需要待处理的数组了
public static void backTracking(int n, int k, boolean[] used) {
//2. 回溯终止条件
if(path.size() == n) {//得到了一个排列
count++;
if(count == k) {//题目要求输出第k个就好
for(Integer i : path) System.out.print(i);
System.out.println();
return;
}
return;
}
//3. 单层遍历!!!!!!!重点
for(int i = 1; i <= n; i++) {
//1. 操作
if(used[i]) continue;//当前元素已经使用过
used[i] = true;
path.push(i);
//2. 回溯
backTracking(n, k, used);
//3. 恢复现场
used[i] = false;
path.pop();
}
}//backtracking
if(str_arr[i].contains('-')) {}//会报错
if(str_arr[i].contains('-' + "")) {}//这么写才对
动态规划
方法1:循环统计
方法2:利用二进制的位数——自己想到的
例如:2 = 10(2) 4 = 100(2) 8 = 1000(2),它们的幂次就是二进制字符串长度-1;
int n = 8;
int mici = Integer.toBinaryString(n).length() - 1;
所以:如果是2的幂次方,那么二进制就只有最高位为1;计算是几次方如上所述。
思路:二进制的位数
例如:13 = 1101(2),一共4位二进制,所以需要对比二进制数b1 = 1000(2)和b2 = 10000(2)所对应的十进制与13的差值即可;即计算23 和24与13的差值,然后对比。
int i = 9;
String bi_str = Integer.toBinaryString(i);
int a = (int)Math.pow(2, bi_str.length()-1);
int b = (int)Math.pow(2, bi_str.length());
int res = 0;
if(Math.abs(a-i) < Math.abs(b-i)) {
res = a;
}else {
res = b;
}
substring(i):去掉下标i之前的字符,i从0开始
str = str.substring(0);//错
str = str.substring(1);//对-去掉下标1之前的字符,也就是0号字符
/*递推公式*/
dp[i] = 1 + dp[i/2];//i为偶数
dp[i] = Math.min(1 + dp[i-1], 1 + dp[i+1]);//i为奇数
//dp[i]即跟前面相关又跟后面相关,而动态规划只能按一个方向遍历,那动态规划还能用吗?
//转换一下思路:i为奇数时,i+1为偶数,则dp[i+1] = 1 + dp[(i+1)/2];
//所以:
dp[i] = Math.min(1 + dp[i-1], 2 + dp[(i+1) / 2]);//i为奇数
该方法的参数是一维数组,二维数组用不了
审好题,不要先入为主
当有很多要求的时候,可以先统计,再判断
用栈
if(!str.matches("[A-Za-z]*")) {}//“正”的正则不会写,只能反着来
简易KMPstr1中是否包含全部的str2
若想在字符串中分离多位数字时,可以在for循环内套while
(x, offsetY):左边是横坐标x的值,右边是y的偏移量,不是y的值,审好题。
如果发现以你的理解和题目给的样例不同,那就推倒自己的理解,试一下别的理解方式。
问题:需要按ASCII码排序,第一时间想到的是TreeSet 和 TreeMap,但Set 和 Map不能随意获取第i个值。
解决:用List存储,再对List排序。
但数据结构的变化就会带来操作上的变化,没有十全十美的方法,算法都要考虑用空间换时间呢~
连续:只需要一维数组;不连续时才需要二维数组
求交换后的字符串
只能交换两个字符,使得字符串最接近ASCII排序
求最小交换次数
char[] chs = str.toCharArray();
char[] temp = chs;//这样赋值的话,chs改变temp也会边;所以,应该如下:
char[] temp = str.toCharArray();
for 从第一个字符到最后一个字符,每次循环都找到后面第一个比自己小的,然后交换。
如果没有取巧的方法,那就for 循环遍历。
for 循环暴力
思路
for 循环扫描一遍数组,下标0开始,数据类型最好用list而不用数组
偶数位(0, 2…):高-矮才合规,不合规则与后面的换位置
奇数位(1, 3…):矮-高才合规,不合规好与后面的换位置
动态规划
大小写分开
思路清晰,认真分析。
动态规划dp[] 一维
二维数组排序。
审好题,递增or递减看清楚即可。
动态规划–变形
反向求下标可以优化:每求得一个dp[i]就判断一下下标,如果下标等于n,那就输出。
for(int i = weishu - 1, mici = 0; i >= 0; i--, mici++) {
String num_str = next + "";
if(num_str.charAt(i) == '4') {
next += Math.pow(10, mici);
}
}//每一位
1)return
return > 0:递增;return < 0:递减;return = 0:不变。
2)o1, o2
o1:后一个数
o2:前一个数
先按首位排序收集,再排序
5:选择当前屏幕上的所有字母了之后,屏幕上就没有字母了
题目一定要理解好
一个人有多个成绩,乱序输入,如果某同学的成绩少于3次,则不参与排名,否则按照每人成绩最高3个的总和降序排列。成绩相同则按id降序。
成绩统计:一般都需要用到多次用到Map,List。
============================================================================
int a = 0;
int b = 1;
System.out.println(a ^ a);//0
System.out.println(b ^ b);//0
System.out.println(a ^ b);//1
因为哥哥弟弟的两堆积木,异或的结果相等,而这两个结果又异或,结果为0。所以,如果积木总重量异或不为0,那说明不能按题目要求分成两堆。故,需要在分成两堆之前先判断是否能分成两堆。
判断能分成两堆后,遍历一遍数组,找到数组中的最小值min,和所有值的总和sum,sum - min 即为哥哥能分到的最大重量的积木。==但为什么是sum - min,原理还不是很理解。
好好审题
数组需要重新处理一下,使得数据从下标为1开始存储。
审好题。理解好,自己手动模拟。然后再把步骤写出来
二叉树先构造,再按要求遍历
查看区间是否重叠,不重叠就可以可以简单运算
想实现循环链表的功能,但是太复杂了(对现在的我来说),所以用while手动模拟实现了循环链表。
有固定窗口和不固定窗口
符合:end++——拓展窗口
不符:start++——更换窗口起点
判断第i为单词是否相等:将每个单词的第i个字符加入set,看最后set的大小,为1则相同,不为1则不同。
1)多个空格
String[] strs = str.trim().split("\\s+");//多个空格
2)数字、字母
//是否全是
if(str.matches("[0-9]+")) {} //是否全是数字
if(str.matches("[A-Za-z]+")) {} //是否全是字母
if(str.matches("[A-Za-z0-9]+")) {} //是否全是字母、数字
//是否包含
if(str.matches(".*[0-9].*")) {} //是否包含数字
if(str.matches(".*[A-Za-z].*")) {} //是否包含字母
3)如何判断是否包含连字符-
if(str.contains("-")) {
String temp = str.replaceAll("-", "");
System.out.println(str.length() + temp.length());//包含几个连字符
}
list.addAll(list1);
//1. 错误
return null;
//正确
List<Integer> res = new ArrayList<Integer>();
if(root == null) return res;//此时list为空
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if(root == null) return res;
res.add(root.val);
List<Integer> left_list = preorderTraversal(root.left);
if(left_list != null) res.addAll(left_list);
List<Integer> right_list = preorderTraversal(root.right);
if(right_list != null) res.addAll(right_list);
return res;
}//前序
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}//class-Node