算法刷题说明
本博客会记录准备秋招过程中的算法刷题过程,包括自己总结的思路和分析步骤,以这种教别人的方式去总结分析,也是我一直推崇的 费曼学习法 的一种形式。(菜鸡只能用这种方式去不断学习了,如果文中有不对的地方,还请各位大佬多担待,指出问题,我会虚心接受并改正的)
所有的题都是根据 算法随想录 一步步做下去,算法随想录也有b站的视频,全部都是开源的,讲的深入浅出,强烈安利。
我主要使用java进行刷题,所以也会补充一些在刷算法的时候的java的相关知识点,这篇博文主要是记录java的相关知识点,后续的每一个算法内容都会专门生成一个章节,每一个知识点一个章节的发布。
0 刷题的时候需要知道的一些操作
0.0 待做事项
- 使用map实现卡码网的11题
- 使用BufferedReader实现15和16题。
0.1 关于输入输出
0.1.0 补充说明
-
IO流
- 在Java的输入中,是以输入流的形式进入程序,因此无法直接指定输入的类型,仅能读取键盘上的内容,返回一个ASCII码,所以在使用输入流的时候,本质是一串ASCII码,需要将类型强转至char,才能正确显示。
- 在Java的IO流中,键盘所输入的会先存放到缓冲流中,当按下Enter键的时候,将缓重流中的数据写入到内存中,因为此性质,我们无法在键盘输入的过程中直接获取内容。
-
快读和快输出
- Java算法必备(背)之快读快输出
- 为什么要快读和快输出
- 可是当数据比较大的时候(个人经验,洛谷数据过万,PTA数据过两千,建议使用快读),我们快读主要是使用io包的StreamTokenizer类。当数据的数量级为10^5时,用StreamTokenizer类差不多好像是要比Scanner快个300ms左右。
0.1.1 输入的几种形式
-
Scanner对象输入(需要导入java.util.Scanner)
- Scanner中关于next()、nextInt()和nextLine()
- 有一个标准的输入是System.in.read(),和Scanner一样使用的是字符输入流,前者要和异常捕获机制一起用。
import Java.util.Scanner; //定义scanner对象,需要传入参数System.in,这是系统标准输入流 Scanner sc = new Scanner(System.in); //读取一整行的数据转换成String,遇到换行符停止,且换行符也会被读入,光标放在下一行 String line = sc.nextLine(); //读取一个int数据,光标放在该数据后面 int num = sc.nextInt(); //读取一个浮点数据,光标放在该数据后面 int num1 = sc.nextFloat(); int num2 = sc.nextDouble(); //读取一个字符串数据,遇到空格或者换行符停止,光标停止在数据后,同一行。 String str = sc.next();
-
BufferedReader(要导入java.io.BufferedReader)
- 该方法采用的是缓存输入流的形式,
- BufferedReader和正常的io流成员类型基本一致,唯一不同是类的构建,BufferedReader需要使用字符输入流作为基类。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class BufferedReaderTest { public static void main(String[] args) throws IOException { //缓冲输入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //while((line = br.readLine)!=-1) String[] line = br.readLine().split(" "); int n = Integer.parseInt(line[0]), m = Integer.parseInt(line[1]); int[][] nums = new int[n][m]; for ( int i = 0; i < n; i++ ) { line = br.readLine().split(" "); for ( int j = 0; j < m; j++ ) { nums[i][j] = Integer.parseInt(line[j]); } } } }
-
Stream Tokenizer类(对应输出是PrintWriter类)(快读与快输出)
- 【Java基础】StreamTokenizer使用详解
- StreamTokenizer类接收输入流并将其解析为“令牌”,允许一次读取一个令牌。 解析过程由表和多个可以设置为各种状态的标志来控制。 流标记器可以识别标识符,数字,引用的字符串和各种注释样式。
- 从输入流读取的每个字节被视为’\u0000’至’\u00FF’范围内的’\u0000’ ‘\u00FF’ 。 字符值用于查找字符的五个可能属性: 空格 , 字母 , 数字 , 字符串引号和注释字符 。 每个角色都可以有零个或多个这些属性。
- 注意:使用StreamTokenizer类要记得抛出I/O异常。
import java.io.*; //普通用法 StreamTokenizer st =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); st.nextToken(); // 获取下一组标记 默认是按照空格分割的 回车,tab是结束符 int i=(int) st.nval; //st.navl默认解析出的格式是double st.nextToken(); double j=st.nval; st.nextToken(); String s=st.sval; //st.savl默认解析出的格式是String //多组输入 public class Main { public static void main(String[] args) throws IOException { StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); //PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); int a, b; while(in.nextToken() != StreamTokenizer.TT_EOF) // 表示读到了文件末尾 { a = (int)in.nval; in.nextToken(); b = (int)in.nval; //out.println(a + b); System.out.println("a + b = "+(a+b)); } //out.flush(); } }
- 补充说明:(三个常用方法/函数)
- quoteChar(int ch) - 指定当前字符为当前tokenizer中的分隔符,在两个符号之间被当作一个字符串解析。(多个ch在一起时,只认为第一个是分隔符)
- whitespaceChars(int low, int hi) - 字符low与hi之间的所有字符都被当作为空格符,即被认识为tokenzier的分隔符。(多个ch在一起时,被认为是同一个分隔符)
- wordChars(int low, int hi) - 字符low与hi之间的所有字符都被当作为单词的要素。 一个单词是由一个单词要素后面跟着0个或者更多个单词要素或者数字要素。
- 使用的补充说明:
- 如果想要’a’作为分隔符,推荐st.whitespaceChars(‘a’,‘a’);不推荐st.quoterChar(‘a’),因为在多个a时,后者不会把它当成一个分隔符。比如"1234aaa567",后者会分割成"1234"和"567",中间的’a’全部被当成空格,而前者会分割为"1234.0",“aaa567”,会保留’a’。
- 数字和字母连接在一起时会被认为是字符串,比如"aaa123",这会被认为是一个整体字符串。分开则不会,比如"aaa 123"。
- 调用nextToken方法后,该字段包含刚刚读取的令牌类型。 对于单个字符标记,其值为单个字符,转换为整数。 对于带引号的字符串标记,其值为引用字符。 否则,其值为以下之一:
- TT_WORD表示令牌是单词。
- TT_NUMBER表示令牌是一个数字。
- TT_EOL表示已读取行尾。 如果使用参数true调用eolIsSignificant方法,则该字段只能具有此值。
- TT_EOF表示已到达输入流的末尾。
-
标准系统输出System.out
Java学习-System.out.println,system.out.print,system.out.printf的区别
System.out.printf()的使用方法System.out.println(参数) //会换行,参数可以为空,为空则表示只换行 System.out.print(参数) //不会换行,且参数不能为空。 System.out.printf("格式",参数)//按照格式输出参数,f是format的意思。
-
StringBuilder输出
//StringBuilder输出 StringBuilder sb = new StringBuilder(); for ( int i = 0; i<n; i++) { for ( int j = 0; j < m; j++ ) { sb.append(nums[i][j]+" "); } sb.append("\n"); } System.out.println(sb.toString());
-
PrintWriter输出
- 将对象的格式表示打印到文本输出流。 这个类实现了全部在发现print种方法PrintStream 。 它不包含用于编写原始字节的方法,程序应使用未编码的字节流。
- 不像PrintStream类,如果启用自动刷新,它只会在调用的println,printf,或format方法来完成,而不是当一个换行符恰好是输出。 这些方法使用平台自己的行分隔符而不是换行符。
- 这个类中的方法不会抛出I / O异常,尽管它的一些构造函数可能。 客户可以通过调用checkError()查询是否发生错误。
import java.io.*; public class test { public static void main(String args[]){ PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out)); pw.print();//不换行输出 pw.println();//换行输出 pw.printf();//格式化输出 pw.flush();//关闭输出流 } }
- PrintWriter类的使用相对而言比较简单,就是把我们平时的输出的System.out替换成对应的快输出的实例对象名,唯一需要比较注意的就是最后记得flush(),如果不使用flush(),它会一直不输出,只放在输出缓存流里面。
0.2 关于字符和字符串
0.2.1 字符串和字符的互换
-
String转换为char
String str = "we are friend"; //方法一,使用charAt() int index = 1; char ch = str.charAt(index);//会把index为1的字符转换为字符,在这里是e。 //方法二,使用toCharArray(),把整个string转换成字符串数组char[] char[] chArray = str.toCharArray(); char ch = chArray[index];
-
char转换成String
//1.效率最高的方法,使用valueOf() String s = String.valueOf('c'); //2.将一个char数组转换成String String s = String.valueOf(new char[]{'c'}); //3.Character.toString(char)方法实际上直接返回String.valueOf(char) String s = Character.toString('c'); //4.类似3 String s = new Character('c').toString(); //5.效率最低 String s = "" + 'c'; // 虽然这个方法很简单,但这是效率最低的方法 // Java中的String Object的值实际上是不可变的,是一个final的变量。 // 所以我们每次对String做出任何改变,都是初始化了一个全新的String Object并将原来的变量指向了这个新String。 // 而Java对使用+运算符处理String相加进行了方法重载。 // 字符串直接相加连接实际上调用了如下方法: // new StringBuilder().append("").append('c').toString(); //6.使用string对象新建 String s = new String(new char[]{'c'});
补充说明:
* 字符/字符数组转字符串,不能直接toString(),对于字符来说,没有这样的方法,对于字符数组来说,本身是一个数组,toString()方法是输出其地址,除非重写了该方法。
0.2.2 字符串和数字的互换
- 数字转字符串
// 数字转字符串 method1 int number = 5; String str = String.valueOf(number); System.out.println(str); // 数字转字符串 method2 int number = 5; Integer itr = number; //int装箱为对象,再调用对象的toString方法 String str = itr.toString(); //或者直接 String str = Integer.toString(number); System.out.println(str); // 数字转字符串 method3 int number = 5; String str = number + ""; System.out.println(str);
- 字符串转数字
// 字符串转数字 String str = "123"; //方法一 Integer num1 = new Integer(str); //方法二 int num2 = Integer.parseInt(str); //方法三 Integer num3 = Integer.valueOf(str);
- 数字、字符互相
// 数字转字符 // 不能直接转换,如下图。因此需借助数字转字符串。 // 首先将数字转为字符串,再获取字符 int number = 5; //注意这里数字只能是个位数并且不能为负 String temp = Integer.toString(number); char ch = temp.charAt(0); System.out.println("数字转字符:"+ch); // 字符转数字 // 先把字符转为字符串,再转换为数字 char ch = '5'; String temp = String.valueOf(ch); int a = Integer.parseInt(temp); System.out.println(a); //或者 int a = ch - '0';
0.2.3 字符串操作函数
-
大小写转换----String.toLowerCase() 或 String.toUpperCase()
- toLowerCase()方法将String转换为小写。如果字符串中没有应该被转换的字符,则将原字符串返回,否则返回一个新的字符串。
- 语法:str.toLowerCase()
- toUpperCase()方法将Srtring转换为大写。如果字符串中没有应该转换的字符,则将原字符串返回,否则返回一个新的字符串。
- 语法:str.toUpperCase()
- 说明:使用toLowerCase()方法和toUpperCase()方法进行大小写转换时,数字或非字符不受影响。
-
去除头尾的空格----String.trim()
- 对一个String修剪头尾的空格,比如:" yes you know ".trim(),则会变成 “yes you know”。
-
长度信息
- 对于数组:array.length,数组是一个初始化就确定好长度的数据类型,所以length是它的一个属性。
- 对于字符串:String.length(),
- 对于集合ArrayList来说,是大小: ArrayList.size()
- 因为String和ArrayList都是对象,所以是方法。
0.2.4 字符串补充知识
-
StringBuilder和StringBuffer
- Java StringBuffer 和 StringBuilder 类
- 三者区别:
String 长度大小不可变
StringBuffer 和 StringBuilder 长度可变
StringBuffer 线程安全, StringBuilder 线程不安全
StringBuilder 速度快
-
StringTokenizer
-
StringJoiner(高效拼接字符串)(java.util.StringJoiner)
- 基于StringBuilder,性能和它类似
- 优雅地输出每个一个数组的值在同一行,每个值用 空格 隔开,开始输出 : , 最后输出 。 号。
import java.util.StringJoiner; int[] a = new int[]{1,2,3,4,5} StringJoiner sj = new StringJoiner(" ",":","。"); for(int i : a){ sj.add(i); } System.out.println(sj.toString);
- 在不需要指定 开头和结尾 时,使用String.join()更方便:
String[] strArray = new String[]{"ni","zhen","hao","kan"}; //字符串队列 List<String> strList = new ArrayList<String>(); //添加元素 strList.add("ni"); strList.add("zhen"); strList.add("hao"); //只能使用数组或者list String joiner = String.join(" ",names);
0.3 关于Stack
0.3.1 什么是Stack
-
介绍:
- 栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
- 堆栈只定义了默认构造函数,用来创建一个空栈。 堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法。
-
所处的包:
- java.util.Stack;
-
常用方法描述:(除了Vector的方法外)
-
-
补充说明
- Stack继承于Vector,而Vector是List的实现类,所以Stack可以用List的一些方法。比如查询栈中数目,Stack.size()。
0.4 关于map