快读

快读的一些方法

在做数据结构、算法题的时候会遇见比较大的数,有的时候数据大小已经超过了int所能储存的范围,这样的大数如果比较多情况下如果使用普通的输入或者输出,超时就可能会发生,如果出现这样的错误,在算法时间复杂度没有问题的前提下出现这样的问题是非常令人头疼的一件事情。

这里我将从Java和C++两中语言入手,在减少这样的问题出现

Java

一、StreamTokenizer实现快速输入

需要的jar包

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

定义如下:

StreamTokenizer st =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

StreamTokenizer只能接收数字或字母,如果输入除空格和回车以外的字符(如:!@#$%^&*()[]{})无法识别,会显示null

StreamTokenizer可以获取输入流并根据空格和回车分割成Token(标记),用nextToken方法读取下一个标记

如果标记是字符串,用st.sval获取标记,如果是数字用st.nval获取标记,st.navl是double类型

示例

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

//import java.io.*;


public class Test {
    public static void main(String[] args) throws IOException {
        StreamTokenizer st =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        st.nextToken();
        String s=st.sval;
        System.out.println(s);
        st.nextToken();
        double n=st.nval;
        System.out.println(n);
        /**输入内容
         * hollow 1233
         * 输出内容
         * hollow
         * 1233.0
         */
        
    }
 
}

二、BufferedReader实现快速输入读一行

需要导入的jar

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

定义:

BufferedReader inBuff=new BufferedReader(new InputStreamReader(System.in));

常用方法:

inBuff.readLine()//读取一行内容,返回字符串

实例:

BufferedReader inBuff=new BufferedReader(new InputStreamReader(System.in));
        String s=inBuff.readLine();
        System.out.println("有问题吗"+s+"没有吧");
        /**
         * 输出内容
         * hollow world!
         * 有问题吗hollow world!没有吧
         */

三、PrintWriter实现快速输出

需要的jar包

import java.io.OutputStreamWriter;
import java.io.PrintWriter;

定义如下:

PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

常用方法:

// 输出分为两步:
// 1、先通过print()或println()方法把需要输出的内容放入缓冲区,
// 2、然后通过flush()将缓冲区的内容输出到控制台

print(需要输出的内容)//不换行输出,只是把需要的内容放入缓冲,
println(需要输出的内容)//换行输出
flush()//刷新缓冲区,把缓冲区的内容输出到控制台,

示例:

package CCPC;

import java.io.OutputStreamWriter;
import java.io.PrintWriter;

//import java.io.*;


public class Test {
    public static void main(String[] args) {
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        String s="hollow world";
        int i=12344;
        out.print(s+" "+i);
        out.flush();
        /**
         * 输出内容
         * hollow world 12344
         */
    }
}

一个例子
一般建议使用BufferedRead()

// https://www.acwing.com/problem/content/790/
// 使用BufferedReader读入的方法

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Ex4 {
    // 全局变量
//    static int N = 100010;  // 数据规模为 10w
//    static int[] arr = new int[N];
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        // parseInt将字符串转为int
        // parseDouble将字符转为double
        int n = Integer.parseInt(reader.readLine());
        String[] arrStr = reader.readLine().split(" ");
        int[] arr = new int[n];

        for (int i = 0; i < n; ++ i) {
            arr[i] = Integer.parseInt(arrStr[i]);
        }
        reader.close();

        long ans = mergesort(arr, 0, n - 1);

        System.out.println(ans);
    }

    private static long mergesort(int[] arr, int l, int r) {
        if (l >= r) return 0;

        int mid = l + r >> 1;
        long res = mergesort(arr, l, mid) + mergesort(arr,mid + 1, r);

        int[] temp = new int[r - l + 1];
        int k = 0, i = l, j = mid + 1;

        while (i <= mid && j <= r) {
            if (arr[i] <= arr[j]) temp[k ++] = arr[i ++];
            else {
                res += mid - i + 1;
                temp[k ++] = arr[j ++];
            }
        }

        while (i <= mid) temp[k ++] = arr[i ++];
        while (j <=   r) temp[k ++] = arr[j ++];

        for (i = l, j = 0; i <= r; i ++, j ++) {
            arr[i] = temp[j];
        }

        return res;
    }
}

C++

getchar读入

inline int read()
{
    register int x = 0 , ch = getchar();
    while( !isdigit( ch ) ) ch = getchar();
    while( isdigit( ch ) ) x = x * 10 + ch - '0' , ch = getchar();
    return x;
}

用时28.74s

fread读入优化

struct io
{
    char ibuf[900 << 20] , * s;
    io()
    {
        fread( s = ibuf , 1 , 900 << 20 , stdin );
    }
    inline int read()
    {
        register int u = 0;
        while( * s < 48 ) s++;
        while( * s > 32 )
            u = u * 10 + * s++ - 48;
        return u;
    }
} ip;

用时1.535s

当然如果觉得以上的方法比较麻烦可以:

std::ios::sync_with_stdio(false);

通过对cin和cout的解除绑定可以达到和scanf于printf一样的速度有时候会更快

输出:

cout

用时102.2s

printf

用时27.36s

puts(直接当字符串输出)

用时2.001s

putchar输出优化

void print(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
        print(x/10);
    putchar(x%10+'0');
}

用时3.339s

fwrite输出优化

template< class T >
    inline void print( register T u )
    {
        static int * q = a;
        if( !u ) * t++ = 48;
        else
        {
            if( u < 0 )
                * t++ = 45 , u *= -1;
            while( u ) * q++ = u % 10 + 48 , u /= 10;
            while( q != a )
                * t++ = * --q;
        }
    }

用时0.6653s

由此可以看出,fwrite对比其他读入方式有极大的差距

不过由于一般输出量都比较小,所以输出优化意义不大

inline,define,register

register就是把东西放到寄存器里面

比如for( int i = 1 ; i <= n ; i++ )

可以写成for( register int i = 1 ; i <= n ; i++ )

但是这个不一定有用,因为这个int可能编译器会帮你放寄存器里面

for register short可能比for register int慢,因为short的寄存器更少..
大家for循环里面还是int比较好

register在大部分情况下(特别是开了O2的时候)并没有明显的优化效果,如果加错了反而可能负优化

如果一个简单函数调用次数很多,属于瓶颈,调用时的传参可能会大大影响程序效率

这种情况下可以把这个函数给inline掉

inline就是把一个函数内联,可以减少跳转和传参的代价

但是如果这个函数过于复杂,可能并不会inline,可以考虑attribute ( ( always_inline ) )或者手动展开,或者define掉(注意define别爆炸)

参考博客:


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!