LeetCode 157. 用 Read4 读取 N 个字符
题目描述
题意分析
已有接口
read4,每次从文件当前位置读取最多4个字符,写入传入的缓冲区,并返回实际读到的数量。需要用它实现一次最多向目标缓冲区写入n个字符的read,返回实际写入量。文件可能不足
n个字符,也可能比需求更长。要同时遵守文件的实际长度与本次请求上限,不能把底层读取量直接当作交付量。
解法:分批 read4 拷贝
核心思路
[!blue]
准备长度为
4的临时缓冲tmp,让read4总是先读入这里。用copied表示已经写入目标缓冲的字符数,它同时也是下一次复制的目标起点。本轮
read4返回cnt后,只有tmp的前cnt个字符有效,而本次请求还需要n - copied个字符。因此实际复制量应为take = min(cnt, n - copied),复制完成后只把take加入copied。当
cnt < 4时,说明底层已读到文件末尾,但这批仍可能包含有效字符,所以必须先复制,再结束。若恰好读到4个,单凭这个数量无法判断文件是否还有内容;只要需求未满足,就继续调用,下一次返回0也能正常结束。本题的
read只调用一次,最后一批超过请求数量的字符不需要留给后续调用。达到copied == n时直接停止,不再进行无用的底层读取。
解题步骤
- 创建四字符临时缓冲,将已交付数量
copied设为0。- 当
copied < n时,调用read4(tmp),得到本批有效字符数cnt。- 计算
take = min(cnt, n - copied),把这部分字符复制到目标缓冲的copied位置,并增加已交付数量。- 若
cnt < 4,文件已结束,跳出循环;否则继续由剩余需求决定是否读取下一批。- 返回
copied,而不是请求的n或所有批次的底层读取量之和。
代码实现
public class Solution extends Reader4 {
public int read(char[] buf, int n) {
char[] tmp = new char[4];
int copied = 0;
while (copied < n) {
int cnt = read4(tmp);
// 本批最多交付有效字符数与剩余需求中的较小值。
int take = Math.min(cnt, n - copied);
System.arraycopy(tmp, 0, buf, copied, take);
copied += take;
// 先复制本批有效字符,再根据短读取判断文件结束。
if (cnt < 4) {
break;
}
}
return copied;
}
}
func read(buf []byte, n int) int {
tmp := make([]byte, 4)
copied := 0
for copied < n {
cnt := read4(tmp)
// 本批最多交付有效字符数与剩余需求中的较小值。
take := cnt
if n-copied < take {
take = n - copied
}
copy(buf[copied:], tmp[:take])
copied += take
// 先复制本批有效字符,再根据短读取判断文件结束。
if cnt < 4 {
break
}
}
return copied
}
复杂度分析
- 时间复杂度:上界为 $O(n+1)$,最多交付 n 个字符,文件提前结束时更早停止。
- 空间复杂度:$O(1)$,固定四字符缓冲,目标输出不计入辅助空间。
关键点总结
[!green]
- 临时缓冲接收底层批次,目标缓冲只接收用户本次需要的部分。
cnt表示本批读到多少,take表示本批交付多少,copied表示累计交付多少。- 结束条件有两个:请求已满足,或最后一批有效字符已处理完且文件到尾。
易错点总结
[!yellow]
- 读到不足四个字符时不能立即退出,必须先复制这批有效内容。
- 不能总是复制四个字符,也不能总是复制
cnt个;最后一批还可能受剩余请求数量限制。copied只能增加实际复制量,按cnt增加可能使返回值超过n。- 多次调用版本需要保留尚未交付的字符,本题单次调用的局部缓冲不能直接代替那种缓存设计。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 158. 用 Read4 读取 N 个字符 II - 多次调用 | 困难 | 原题read可多次调用,多读出的字符必须跨调用缓存;本题只调用一次。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!