页面正在赶来的路上……

基础算法——快速读写


快速读写(以C语言为例)

参考:快读攻略(详) - 学委 的博客 - 洛谷博客 (luogu.com.cn)

C语言原本的输入输出函数scanfprintf由于考虑了很多复杂情况(例如①格式化字符串解析②错误处理③缓冲区刷新等),导致在读写位数较大的数据时速度较慢,故自己写一些速读、速写函数增加特定程序的运行速度

1. 快读

1.1 快读原理

  • 数字之间用空格或者换行符来分隔。

  • 关于负数,负号后面是它的绝对值。

  • 由于数字正向给出,所以要读入 256 可以这样:

    • 读入 2,记录 2。
    • 读入 5,先把之前的记录乘以 10,然后加上 5。现在记录了 25。
    • 读入 6,先把之前的记录乘以 10,然后加上 6。现在记录了 256。
    • 读到空格、换行符或者 EOF(读不到东西),就可以返回结果:256。
  • 我们用 getchar() 逐个读进数字字符,再减去字符 ‘0’(ASCII码为 48) 即得对应的数字。

1.2 基础实现

#include 
#include 
int getint()
{
    int res = 0, neg = 0, ch = getchar();
    while(!(isdigit(ch) or ch == '-') and ch != EOF)
        ch = getchar();
    if(ch == '-')
        neg = 1, ch = getchar();
    while( isdigit(ch) )
        res = res * 10 + (ch - '0'), ch = getchar();
    return neg ? -res : res;
}

程序分析:现在该段程序对时间复杂度影响最大的为getchar()函数

1.3 利用fread优化普通快读(优化getchar()函数)

1.3.1 fread()函数详解

size_t fread(void *ptr, size_t size, size_t count, FILE *stream);

fread函数的作用是从指定的文件流 stream 中读取数据,并将读取的数据存储到 ptr 指向的内存块中。它的参数含义如下:

  • ptr:指向存储读取数据的内存块的指针。

  • size:每个元素的大小(字节数)。

  • count:要读取的元素个数。

  • stream:要读取数据的文件流。

1.3.2 利用fread()函数替代基础实现中的getchar()函数

#include 

#define BUFSIZE (1 << 20) // 1048576

char tmp[BUFSIZE];
int cnt = 0, Max = 0;

char getch()
{
    if (cnt == Max)	//第一次输入的情况
    {
        cnt = 0;
        Max = fread(tmp, 1, BUFSIZE, stdin);
    }
    return tmp[cnt++];
}

1.3.3 嵌入getch()的指针写法快读

#include 
#include 

#define BUFSIZE (1 << 20)

char ibuf[BUFSIZE], *is = *ibuf[0], *it = *ibuf[0];

char getch() {
    if (is == it) {
        it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin);
    }
    return (is == it) ? EOF : *is++;
}

int getint() {
    int res = 0;
    int neg = 0;
    int ch = getch();
    
    while (!(isdigit(ch) || ch == '-') && ch != EOF) {
        ch = getch();
    }
    if (ch == '-') {
        neg = 1;
        ch = getch();
    }
    while (isdigit(ch)) {
        res = res * 10 + (ch - '0');
        ch = getch();
    }
    return neg ? -res : res;
}

编译运行、键盘输入数据时 fread() 无法停止?因为它期望得到太多字符,键盘输入时它不知道什么时候是末尾,所以你得告诉它“已经结束输入”。Ctrl + Z 后再回车就好了。

2. 快输

2.1 快输原理

  • 关于负数,在负号后方输出绝对值。

  • 正向输出一个数字。通过模 10 的方式我们可以直接获得它的最后一位,然后整个数除以 10 使原来最后第二位变成最后一位。但是注意这是获取了最后一位,因此不能直接逐位输出,要暂存后倒序输出

  • 第2步中,我们不断循环获取最后一位,退出条件是当前数剩余 0,所以,如果开始时这个数就是 0,就要特判后输出一个 0。0 确实是一个特殊的数,人为规定了用一个字符表示它。

  • putchar() 逐位输出数字。要输出的是数字字符,而我们模 10 后得到的是数,因此输出时要加上字符 ‘0’(或 48)。“当同一行中有多于一个元素时,须用单个空格符以作分隔。”

  • 间隔符号,如空格、换行,要另外输出。

2.2 基础实现

inline void putint(int res)
{
    char q[10];
    
    if(res == 0)
        putchar('0');
    else
    if(res < 0)
    {
        putchar('-');
        res = -res;
    }
    
    int top = 0;
    while(res)
    {
        q[top++] = res % 10 + '0';
        res /= 10;
    }

    while(top--)
        putchar(q[top]);
}

2.3 利用fwrite优化普通快输(优化putchar()函数)

2.3.1 fwrite()函数详解

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

fwrite函数接受四个参数:

  • ptr:指向要写入数据的内存块的指针。

  • size:要写入的每个数据项的大小(以字节为单位)。

  • count:要写入的数据项的数量。

  • stream:指向目标文件的指针。

fwrite函数将count个数据项(每个大小为size字节)从ptr指向的内存块写入到由stream指向的文件中。它返回成功写入的数据项数量。如果返回的数量与count不一致,可能表示写入过程中发生了错误。

2.3.2 利用fwrite()函数替代基础实现中的putchar()函数

#include 

#define BUFSIZE (1 << 20)
char tmp[BUFSIZE];
int cnt = 0;

void flush()
{
    fwrite(tmp, 1, cnt, stdout);
    cnt = 0;
}

void putch(char ch)
{
    tmp[cnt++] = ch;
    if (cnt == BUFSIZE)
        flush();
}


文章作者: Z
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Z !
评论
//
  目录