刷题中常见和高效API小算法(持续更新)
一.求最大公约数
1.辗转相除法(欧几里德算法)
非递归实现
public static int solve(int m, int n) {
int result = 0;
while (n != 0) {
result = m % n;
m = n;
n = result;
}
return m;
}
递归实现
static int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
2.更相减损术
public static int gcd(int m, int n) {
while (m != n) {
if (m > n)
m -= n;
else
n -= m;
}
return m;
}
二.求最小公倍数
private static int lcm(int m, int n) {
int M = m;
int N = n;
while(true){
if((m = m % n) == 0){
return M*N/n;
}
if((n = n % m ) == 0){
return M*N/m;
}
}
}
//或者利用gcd()求lcm
static int lcm(int m,int n){
return m*n/gcd(m,n);
}
递归求全排列
//递归求1-9的全排列
public static void main(String args[]){
int []arr={1,2,3,4,5,6,7,8,9};
f(arr,0);
}
private static void f(int[] arr, int k) {//arr为需要求全排列的序列数组,k为递归的层数也是判断退出的条件
if(k==9){
//check(arr);此处可以为得到全排列后进行的操作函数,如打印
return;
}
for(int i=k;i<arr.length;i++){
//将第i位和第k位交换
int t=arr[i];
arr[i]=arr[k];
arr[k]=t;
//下一层
f(arr,k+1);
//回溯(换回来)
t=arr[i];
arr[i]=arr[k];
arr[k]=t;
}
}
StreamTokenizer(输入,在相同足够大的输入情况下,相比普通Scanner对象输入,速度近快了3倍)
针对于对时间严格的题的输入,JAVA解题一般用Scanner类来进行输入,但对时间要求严格的题,可能会超时,这个时候就需要StreamTokenizer,搭配BufferedReader,以及PrintWriter实在是居家旅行杀人灭口必备良药,那么怎样的题需要用到这两个“乖乖”呢,如果不是今天我刷了这道题,我也不知道还有这么两个东西。
就是这种看起来很简单的题,实则暗藏杀机,我都觉得是测评姬的问题(手动狗头),c++代码两个循环能过,为什么java两个循环不能过????
StreamTokenizer可以获取输入流并将其分析为Token(标记),StreamTokenizer的nextToken方法读取下一个标记 ,具体查看API,总之这个类的作用就是套上缓冲流,提高输入的效率,那么怎么提高输出的效率呢,就是下一个"乖乖"了
PrintWriter(输出)
PrintWriter可用来作为文件的输出,套上System.out也可做控制台输出,具体用法和System.out大致一样,但是就是比System.out快,实测有效,当时我在提交下面代码的时候,使用了StreamTokenizer,故意不用PrintWriter,而用System.out,运行超时,换成了PrintWriter,答案正确,艹(是一种植物),常用方法
(1)print(String str):向文件写入一个字符串。
(2)print(char[] ch):向文件写入一个字符数组。
(3)print(char c):向文件写入一个字符。
(4)print(int i):向文件写入一个int型值。
(5)print(long l):向文件写入一个long型值。
(6)print(float f):向文件写入一个float型值。
(7)print(double d):向文件写入一个double型值。
(8)print(boolean b):向文件写入一个boolean型值。
具体查看API.
Java能AC的代码
import java.io.*;
import java.util.Scanner;
public class xm_2{
public static void main(String[] args) throws IOException {
//Scanner sc=new Scanner(System.in);
StreamTokenizer st =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
//int n=sc.nextInt();
st.nextToken();//开始解析,准备读入
int n=(int)st.nval;//读入一个数字标记
String s[]=new String[n];
for(int i=0;i<n;i++){
st.nextToken();//每次读入前都需要这个语句
s[i]=st.sval;//读入文字标记
}
for(int i=0;i<n;i++){
for(int j=n-1;j>=0;j--){
//System.out.print(s[j].charAt(i));
out.print(s[j].charAt(i));
}
out.println();
}
out.flush();
}
}
我要为Java报不平,如果有大佬知道为什么Java正常使用两个循环会超时,而C++不会超时,欢迎交流
任意进制转换
当数字为十进制的时候认为是 int (或者long,BigInteger…)是个数字类型。
当数字为其他进制的时候则被认为 是String 类型
由任意进制为十进制
调用JavaAPI中的XXX.parseXXX方法,可将相应数据类型上的数据转换为对应的十进制数,如:
//将16进制的数转换为10进制的数
public static void Main(String []args){
String s = "A";
int num = Integer.parseInt(s,16);
System.out.println(num);
}
同理也可转换其他进制的数,及其他类型的数据,如long,double等。
由十进制转换为任意进制的数
调用API中的toString方法,第一个参数是要转换的数字,第二个参数是要转换成为的进制。
public static void Main(String []args){
int num = 8;
String s = Integer.toString(num,2);
System.out.println(s);
}
或者调用包装类中的toxxxString方法,如
10进制转化其他进制 对应的方法,参数:n(原10进制数据),r(进制), 返回值
10进制转2进制 Integer.toBinaryString(n); 一个二进制字符串.
10进制转8进制 Integer.toOctalString(n); 一个八进制字符串
10进制转16进制 Integer.toHexString(n); 一个16进制字符串
10进制转 r 进制 Integer.toString(100, 16); 一个r进制字符串
附:
Integer.bitCount(int n); //返回n在二进制下中1的个数,返回int
求素数打表
Eratosthenes筛法(据说能跑1e6的数据)
埃拉托斯特尼筛法,简称埃氏筛,埃筛。这是一种简单且历史悠久的筛法,用来找出一定范围内所有的素数。所使用的原理是从2开始,将每个素数的各个倍数,标记成合数。一个素数的各个倍数,是一个差为此素数本身的等差数列。此为这个筛法和试除法不同的关键之处,后者是以素数来测试每个待测数能否被整除。
给出要筛数值的范围n,找出根号n以内的素数p1,p2,…,pk。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去……虽然它能处理1e6的数据,已经非常优秀了,但是还是有劣势,那就是已经筛选过的数可能会被再筛选一遍
static boolean bis[] = new boolean[(int)1e6];//在这里我规定为true表示素数.
static void aiShai(int n){//计算2-n之间有多少素数
Arrays.fill(bis,true);//初始化为全是true,一开始都是素数,然后再筛去不是素数的数,将其置为false
for(int i = 2;i*i<=n;i++){
if(bis[i]){//如果这个素是素数
for(int j=i*i;i<=n;j+=i){
//j 从 i * i 而不是从 i + i开始,因为 i*(2~ i-1)在 2~i-1时都已经被筛去,所以从i * i开始,这是一个小优化。
bis[j]=false;//将素数的倍数全部设为false,即不是素数
}
}
}
}
欧拉筛法(线性筛)(据说能跑1e7的数据)
在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。
static boolean vis[] = new boolean[(int)1e6];
static int prime[] = new int[(int)1e6];
static int cnt = 0;//个数
static void eulerSieve(int n){
Arrays.fill(vis,true);
for (int i = 2;i*i<=n;i++){
if (vis[i]){ //不是目前找到的素数的倍数
prime[cnt++]=i; //找到素数
for (int j=0;j<cnt&&i*prime[j]<=n;j++){
vis[i*prime[j]]=false;//找到的素数的倍数不访问
if(i%prime[j]==0){//关键
break;
}
}
}
}
}
对于visit[i*prime[j]] = 1 的解释: 这里不是用i的倍数来消去合数,而是把 prime里面纪录的素数,升序来当做要消去合数的最小素因子。
对于 i%prime[j] == 0 就break的解释 :当 i是prime[j]的倍数时,i = kprime[j],如果继续运算 j+1,i * prime[j+1] = prime[j] * k prime[j+1],这里prime[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。
举个例子 :i = 8 ,j = 1,prime[j] = 2,如果不跳出循环,prime[j+1] = 3,8 * 3 = 2 * 4 * 3 = 2 * 12,在i = 12时会计算。因为欧拉筛法的原理便是通过最小素因子来消除。
具体可以看看这篇文章