1. 入门
1. 求int型正整数在内存中存储时1的个数
题目描述:
输入一个int型的正整数,计算出该int型数据在内存中存储时1的个数。
输入: 输入一个整数(int类型)
输出: 这个数转换成2进制后,输出1的个数
示例: 5 得到 2
解法一:
正整数转换成二进制,每次整除2,直到商为0,每次得到的余数(0或者1)组合起来就是它的二进制
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int count = 0;
while(sc.hasNextInt()){
int input = sc.nextInt();
while(input!=0){
// input%2是余数
count = count + input%2;
// input/2是商
input = input >> 1;
// input = input/2;
// 用移位操作代替除法
}
// Ouput
System.out.println(count);
}
}
} 解法二:
在位操作中,数字自动是按照二进制运算的。
例如7表示为0111
n&(n-1) 即(0111)&(0110)== 0110 就是 n去除了最后一个1 ;
几个1 就可以在几次内 去除几个1;
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int count = 0;
while(sc.hasNextInt()){
int input = sc.nextInt();
while(input!=0){
input = input & (input-1);
count++;
}
}
// Ouput
System.out.println(count);
}
} 2. 近似值
题目描述:
写出一个程序,接受一个正浮点数值,输出该数值的近似整数值。如果小数点后数值大于等于5,向上取整;小于5,则向下取整。
输入:输入一个正浮点数值
输出:输出该数值的近似整数值
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNextDouble()){
double input = sc.nextDouble();
// java 的数据强制转换,是同意向下取整
System.out.println( (int)(input+0.5) );
}
}
} 2. 简单
1. 棋盘路径
题目描述:
请编写一个函数(允许增加子函数),计算n x m的棋盘格子(n为横向的格子数,m为竖向的格子数)沿着各自边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往上走。
输入描述: 输入两个正整数
输出描述: 返回结果
解法: 考虑棋盘的走法,从起点出发到达终点,无论走何种路线都要走(m+n)步且经过(m+n)个交叉路口决定走的方向,其中m步向下,n步向右。
数学中的组合可以解决这个问题,一共(m+n)次决定,其中任意选择m次向下走,其余的n次必然向右走,一共有 种走法, 公式计算为
import java.util.Scanner;
public class Main{
public static int factorial(int num){
if(num==0 || num==1){
return num;
}
int res = 1;
while(num!=0){
res = res * num;
num--;
}
return res;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNextInt()){
int n = sc.nextInt();
int m = sc.nextInt();
int res = factorial(n+m)/(factorial(m)*factorial(n));
System.out.println(res);
}
}
} 2. 字符串逆序
题目描述:
将一个字符串str的内容颠倒过来,并输出。str的长度不超过100个字符。 如:输入“I am a student”,输出“tneduts a ma I”。
解法: 题目本身很简单,但是我这个菜鸡在学习别人的解法时发现了很多java的语法知识点,特此记录,string与stringBuilder的区别
解法一: String 转成 char array 再逆序, 缺点: 数组越界,菜
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
char[] str = sc.nextLine().toCharArray();
char[] rev = new char[str.length];
for(int i=0; i<str.length; i++){
rev[i] = str[str.length-i-1];
}
for(int i=0; i<str.length; i++){
System.out.print(rev[i]);
}
}
} 解法二: 直接用StringBuilder的reverse()方法
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();//next()是遇到空格;nextLine()是遇到回车
StringBuilder sb = new StringBuilder(str);
System.out.println(sb.reverse());
} 解法三: 改进解法一
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();//next()是遇到空格;nextLine()是遇到回车
StringBuilder sb = new StringBuilder();
for(int i=0; i<str.length(); i++){
sb.append(str.charAt(str.length()-i-1));
}
System.out.println(sb);
}
} 解法四: 用栈的逆序性质;
解法五: toCharArray,再逆序输出
3. 输入字符串的四则运算
快速解法:
//😄 😄 print(eval(input()))
// 用javascript里面内置的eval函数做
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.Scanner;
public class Main
{
public static void main(String args[]) {
String enter;
Scanner in = new Scanner(System.in);
while (in.hasNextLine()){
enter = in.nextLine();
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
try {
Object result = engine.eval(enter);
System.out.println(result);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
} a) 括号匹配
参考力扣第20题解析
题目描述:
保证输入字符串中的括号'(',')','{','}','[',']'是否有效。也就是说,
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
题目解析:
- 栈先入后出特点恰好与本题括号排序特点一致,即若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,则遍历完所有括号后 stack 仍然为空;
- 建立哈希表 dic 构建左右括号对应关系:key左括号,value右括号;这样查询 22 个括号是否对应只需 O(1) 时间复杂度;
- 建立栈 stack,遍历字符串 s 并按照算法流程一一判断。
算法流程:
- 先判断当前字符是括号么
- 如果 c 是左括号,则入栈 pushpush;
- 否则通过哈希表判断括号对应关系,若 stack 栈顶出栈括号
stack.pop()与当前遍历括号c不对应,则提前返回false。
public static boolean isValid(String s) {
// 建立哈希表
HashMap<Character, Character> myMap = new HashMap<Character, Character>();
myMap.put('(', ')');
myMap.put('{', '}');
myMap.put('[', ']');
HashSet<Character> charSet = new HashSet<>();
charSet.add('(');
charSet.add(')');
charSet.add('[');
charSet.add(']');
charSet.add('{');
charSet.add('}');
// 利用辅助栈
LinkedList<Character> myStack = new LinkedList<>();
// 遍历字符串
for(int i=0; i<s.length(); i++){
// 当前字符
char c = s.charAt(i);
// 判断是否是括号集合里面的元素
// 非括号元素
if (charSet.contains(c)){
// 判断当前字符是否是开括号,如果是则入栈
// 如果闭括号,则判断此时栈是否为空,若为空则返回false
// 若不为空,则判断弹出字符是否是开括号所对应的闭括号
if(myMap.containsKey(c)){
myStack.addLast(c);
}else{
if(myStack.isEmpty() || myMap.get(myStack.removeLast())!=c){
return false;
}
}
}else continue;
}
return myStack.isEmpty();
} b) 逆波兰表达式
平时我们习惯将表达式写成 (1 + 2) * (3 + 4),加减乘除等运算符写在中间,因此称呼为中缀表达式。
而波兰表达式的写法为 (* (+ 1 2) (+ 3 4)),将运算符写在前面,因而也称为前缀表达式。
逆波兰表达式的写法为 ((1 2 +) (3 4 +) *),将运算符写在后面,因而也称为后缀表达式。
波兰表达式和逆波兰表达式有个好处,就算将圆括号去掉也没有歧义。逆波兰表达式去掉圆括号,变成 1 2 + 3 4 + * 也是无歧义并可以计算的。
如何把中缀表达式转换成后缀表达式,以后有时间再写,参考资料1 和 资料2,先重要的是解决如何把后缀表达式变成数学表达式输出结果:
输入表达式: "3", "4", "+", "5", "*","6","-", 得到(3+4)*5-6=29
思路:
遍历每一个字符,如果是数字就入栈,如果是运算符,就出栈两个数***算,结果再入栈
/*
* 定义一个后缀表达式计算器
* @ parameter:
* List<String> notation -> 包含操作数和运算符的list
* @ return
* int result -> 后缀表达式,逆波兰表达式所得到的结果
*/
public static int suffixCalculator(String[] notation) {
// 计算思路:
// 1. 定义一个栈,用来存储操作数
if (notation == null){
return -1;
}
LinkedList<String> stack = new LinkedList<>();
// 2. 从左向右遍历每个字符
for (String elem : notation){
// 3. 判断当前字符是操作数还是运算符
if (elem.matches("\\d+")){ // 匹配多位数,其中第一个/是转义字符,/d是0-9数字,/d+是多位数
// 4. 操作数,则压入栈中
stack.addLast(elem);
//System.out.println("压入数字: " + elem);
}else {
// 5. 运算符, 则从栈中弹出两个操作数,运算完后再放入栈
int num2 = Integer.parseInt(stack.removeLast());
int num1 = Integer.parseInt(stack.removeLast());
int result = 0;
if(elem.equals("+")){
result = num1 + num2;
} else if (elem.equals("-")){
result = num1 - num2;
} else if (elem.equals("*")){
result = num1 * num2;
} else if (elem.equals("/")){
result = num1 / num2;
} else {
throw new RuntimeException("非法输入操作符");
}
stack.addLast(valueOf(result));
}
// 6. 得到栈中最后一个元素,就是最后的结果
}
return Integer.parseInt(stack.removeLast());
} 4. 杨辉三角变形
题目描述:
1
1 1 1
1 2 3 2 1
1 3 6 7 6 3 1
1 4 10 16 19 16 10 4 1
以上三角形的数阵,第一行只有一个数1,以下每行的每个数,是恰好是它上面的数,左上角数到右上角的数,3个数之和(如果不存在某个数,认为该数就是0)。
求第n行第一个偶数出现的位置。如果没有偶数,则输出-1。例如输入3,则输出2,输入4则输出3。
解法一: 观察法
/* 本题观察数据有规律可循
1
1 1 1
1 2 3 2 1
1 3 6 7 6 3 1
1 4 10 16 19 16 10 4 1
1 5 15 30 45 51 45 30 15 5 1
1 6 21 50 90 126 141 126 90 50 21 6 1
1 7 28 77 161 266 357 393
1 8 36 112
1 9 45 156
*/
public static int ruler(int row) {
//当n<3时,没有偶数,输出-1
//当n为偶数且能被4整除时,第一个偶数位置在第三,输出3
//当n为偶数但不能被4整除时,偶数位置在第四,输出4
//当n为奇数时,第一个偶数位置在第二,输出2
if(row < 3){
return -1;
}else if (row % 2 == 1) {
return 2;
} else if (row % 4 == 0) {
return 3;
} else {
return 4;
}
} 解法二: 构造三角矩阵
利用当前数字等于上三个数字的和,可以迭代算出答案;
注意边界条件,导致遍历从1开始到i-2结束
public static int constructionTriangle(int row){
// 用一个矩阵来表示杨辉三角,n行会有 2*n-1列
int col = 2*row-1;
int[][] matrix = new int[row][col];
// 每一行代表一个杨辉三角行,第一行在最中间有一个1,其他都是0,1的位置在[0][row-1]
matrix[0][row-1] = 1;
// 构造每一行的值,注意为了边界问题,遍历每一行时
// 下标从1开始遍历到倒数第二结束,最后一行的开头和结尾补1
for(int i=1; i<row; i++){
for (int j=1; j<col-1; j++){
matrix[i][j] = matrix[i-1][j-1] + matrix[i-1][j] + matrix[i-1][j+1];
}
}
// 补1, 最后一行没有计算
matrix[row-1][0] = matrix[row-1][col-1] = 1;
// 找到最后一行的第一个偶数位
int evenIndex = -1;
for(int i=0; i<col; i++){
if (matrix[row-1][i] % 2 == 0 && matrix[row-1][i]!=0){
evenIndex = i+1;
break;
}
}
// 若没有找到,自动返回-1
return evenIndex;
} 5. 最小公倍数
解法:
最小公倍数 = a*b / 最大公约数(a,b)
最大公约数用辗转相除法得到,
- a%b得余数c
- 若c=0,则b即为两数的最大公约数
- 若c≠0,则a=b,b=c,再回去执行(1)
public static int getGCD (int a, int b){
// 自动交换 a, b
int mod = 1;
while (mod != 0){
mod = a % b;
if (mod == 0){
break;
}
a = b;
b = mod;
}
return b;
// 递归版本;
//return a%b==0 ? b:getGCD(b, a%b);
}
// 最小公倍数 = x * y / 最大公约数(x,y)
public static int getLCM (int a, int b){
return a*b/getGCD(a,b);
} 3.字符串问题集合
计算整数二进制中1的个数
解法:输入整数变成二进制字符串,再遍历统计'1'的个数
int num = sc.nextInt();
String binaryStr = Integer.toBinaryString(num);
char[] charArr = binaryStr.toCharArray();
int count = 0;
for(char cur : charArr){
if (cur == '1'){
count++;
}
} 大写字符(即'A'-'Z')的个数
解法: 遍历字符检查
public static int getCaptialNumber (String str){
int count = 0;
char[] charArr = str.toCharArray();
for(char cur : charArr){
if(cur>='A' && cur<='Z'){
count++;
}
}
return count;
} 参数解析
NR. HJ 74
请编写一个参数解析程序,实现将命令行各个参数解析出来。
解析规则:
1.参数分隔符为空格
2.对于用“”包含起来的参数,如果中间有空格,不能解析为多个参数。比如在命令行输入xcopy /s “C:\program files” “d:\”时,参数仍然是4个,第3个参数应该是字符串C:\program files,而不是C:\program,注意输出参数时,需要将“”去掉,引号不存在嵌套情况。
3.参数不定长
4.输入由用例保证,不会出现不符合要求的输入
题意理解就是,输入的参数可以按照空格将字符串分割,但是带引号的字符串不是以空格为分隔符而是成对的引号,比如说:
输入字符串是 xcopy /s “C:\program files” “d:\”
按照空格分割就是:
xcopy
/s
“C:\program
files”
“d:\”
但实际题目想要的是:
xcopy
/s
“C:\program files”
“d:\”
解题思路:先用空格将字符串分割,在逐个便利每个,统计其中引号的数量:
- 如果有两个引号,就把引号去除直接加入答案容器中
- 如果有一个引号,则设置一个temp字符串,一致遍历到下一个引号为1的字符串,一起连起来
- 如果没有引号,直接加入答案容器中
public static LinkedList<String> inputParsing(String input){
// 先把字符串按照空格分割
String[] strings = input.split(" ");
// 答案容器
LinkedList<String> res = new LinkedList<>();
// 遍历每个字符串,按照引号的数量处理
int index = 0;
while (index < strings.length){
// 分割的字符包含完整的引号,则直接加入list中
if (stringCount(strings[index]) == 2){
res.add(strings[index].replace("\"", ""));
index += 1;
}else if (stringCount(strings[index]) == 1){
// 只包含一个引号,需要遍历寻找,直到下一个引号也为一个
StringBuilder temp = new StringBuilder(strings[index]);
for (int j = index + 1; j < strings.length; j++){
if (stringCount(strings[j]) == 1){
temp.append(" ");
temp.append(strings[j]);
// 注意这里是j+1,如果是j则就是死循环
index = j+1;
break;
}else {
temp.append(" ");
temp.append(strings[j]);
}
}
res.add(temp.toString().replace("\"", ""));
}else { // 包含零个引号,直接加入
res.add(strings[index]);
index += 1;
}
}
return res;
}
// 统计字符串中引号的个数,并返回数量
public static int stringCount(String str){
int num = 0;
for (char character : str.toCharArray()){
if (character == '\"'){
num += 1;
}
}
return num;
} 日期到天数转换
Nr. HJ 73
详细描述:
输入某年某月某日,判断这一天是这一年的第几天?
示例1
输入
2012 12 31
输出
366
解题思路:
首先要判断输入的年份是否合法,考虑年月日的合法性,和2月在闰年和非闰年是否合法。接下来就是计算天数,公式就是month*当月天数累加再加上最后一个月的day。
判断闰年的方法参考热心网友admin-root
闰年是公历中的名词。闰年分为普通闰年和世纪闰年。
普通闰年: 公历年份是4的倍数的,且不是100的倍数,为普通闰年。(如2004年就是闰年);
世纪闰年: 公历年份是整百数的,必须是400的倍数才是世纪闰年。(如1900年不是世纪闰年,2000年是世纪闰年)
// days[0] 是非闰年,days[1] 是闰年
// 1 2 3 4 5 6 7 8 9 10 11 12
private static final int[][] days = {{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
public static boolean isLeapYear(int year){
// 闰年分世纪闰年和普通闰年
// 世纪闰年:能被400整除的年份
// 普通闰年:能被4整除但不能被400整除
if (year % 400 == 0){
return true;
}else if ((year%100 != 0 && year%4 == 0)){
return true;
}else {
return false;
}
}
public static boolean isValidDate(int year, int month, int day){
// 不符合规定就是:
if (year<0 || month<0 || month>12 || day<0 || day>31){
return false;
}
// 检查特定月份的天数是否符合规定
if (isLeapYear(year)){ // 闰年
if (day > days[1][month]){
return false;
}
}else{ // 非闰年
if (day > days[0][month]){
return false;
}
}
return true;
}
public static int getDays(String str){
// 清洗数据
String[] inputs = str.trim().split(" ");
if (inputs.length != 3){
return -1;
}
int year = Integer.parseInt(inputs[0]);
int month = Integer.parseInt(inputs[1]);
int day = Integer.parseInt(inputs[2]);
// 判断数据是否合法
if (!isValidDate(year, month, day)){
return -1;
}
// 判断是否是闰年
if (isLeapYear(year)){
int dayOfMonth = 0;
for (int i = 1; i < month; i++){
dayOfMonth += days[1][i];
}
return day + dayOfMonth;
}else{
int dayOfMonth = 0;
for (int i = 1; i < month; i++){
dayOfMonth += days[0][i];
}
return day + dayOfMonth;
}
} 4.动态规划
放苹果
解法参考牛客网友,没看懂--
设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论,
当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m)
当n<=m:不同的放法可以分成两类:
1、有至少一个盘子空着,即相当于f(m,n) = f(m,n-1);
2、所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n).
而总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n)
递归出口条件说明:
当n=1时,所有苹果都必须放在一个盘子里,所以返回1;
当没有苹果可放时,定义为1种放法;
递归的两条路,第一条n会逐渐减少,终会到达出口n==1;
第二条m会逐渐减少,因为n>m时,我们会return f(m,m) 所以终会到达出口m==0.
public static int count(int num_apple, int num_dish) {
// 递归出口
// 当dish=1时,所有苹果都必须放在一个盘子里,所以返回1
// 当apple=0(没有苹果可放)时,定义为1种放法
if(num_apple==0 || num_dish==1){
return 1;
}
//因为我们总是让m>=n来求解的,所以m-n>=0,所以让m=0时候结束,如果改为m=1
//则可能出现m-n=0的情况从而不能得到正确解
if(num_dish > num_apple){
return count(num_apple, num_apple);
}else {
return count(num_apple, num_dish-1) + count(num_apple-num_dish, num_dish);
}
} 
京公网安备 11010502036488号