本文由 简悦 SimpRead 转码, 原文地址 tbghg.top
这里是 TBH 的博客,用于技术分享,记录日常。
背景
面试时 ACM 模式较多,力扣以核心代码模式为主,特地训练下 ACM 模式处理输入输出
推荐阅读
举例
在此用洛谷中的一道题说明一下:P1886 滑动窗口 /【模板】单调队列
使用常规输入输出:
折叠代码块 GO 复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package main
import ( "fmt" "strings" )
func main() { var n, k int fmt.Scanln(&n, &k) list := make([]int, n) for i := 0; i < n; i++ { fmt.Scan(&list[i]) } max, min := slidingWindow(list, k) fmt.Println(strings.Trim(fmt.Sprint(min), "[]")) fmt.Println(strings.Trim(fmt.Sprint(max), "[]")) }
func slidingWindow(list []int, k int) (max, min []int) { var maxWindow, minWindow []int maxPush := func(i int) { for len(maxWindow) > 0 && list[maxWindow[len(maxWindow)-1]] < list[i] { maxWindow = maxWindow[:len(maxWindow)-1] } maxWindow = append(maxWindow, i) } minPush := func(i int) { for len(minWindow) > 0 && list[minWindow[len(minWindow)-1]] > list[i] { minWindow = minWindow[:len(minWindow)-1] } minWindow = append(minWindow, i) } for i := 0; i < k; i++ { maxPush(i) minPush(i) } max = append(max, list[maxWindow[0]]) min = append(min, list[minWindow[0]]) for i := k; i < len(list); i++ { maxPush(i) minPush(i) for maxWindow[0] < i-k+1 { maxWindow = maxWindow[1:] } for minWindow[0] < i-k+1 { minWindow = minWindow[1:] } max = append(max, list[maxWindow[0]]) min = append(min, list[minWindow[0]]) } return }
|
结果就是有三个超时,其他通过,可自行尝试
分析下原因,每次fmt.Scan
时都会进行一次系统调用,如果 N 较大,那时间开销相对就比较大,我们可以使用bufio
库将数据直接读取到缓存中,整体只需要一次系统调用,时间开销要小很多
相应的,输出时如果我们选择 for 循环每次 print 一个数据,那时间开销也会很长,但将数据通过strings.Trim(fmt.Sprint(min), "[]")
处理好之后再输出,总共两次fmt.Println
系统开销要小很多。同时我们也可以把结果放到缓冲区中,再通过Flush
将数据写出
我们先来看处理输入:
折叠代码块 GO 复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import ( "bufio" "fmt" "os" "strings" )
func main() { var n, k int fmt.Scanln(&n, &k) list := make([]int, n)
in := bufio.NewReader(os.Stdin) for i := 0; i < n; i++ { fmt.Fscan(in, &list[i]) }
max, min := slidingWindow(list, k) fmt.Println(strings.Trim(fmt.Sprint(min), "[]")) fmt.Println(strings.Trim(fmt.Sprint(max), "[]")) return }
|
修改后,所有数据点可通过
我们再来看下输出,这时我们用了两次fmt.Println
,也就是会进行两次系统调用,那我们如果将结果写入输出的缓存中,最后os.Flush
是不是可以对时间进一步优化呢,原则上是这样的:
折叠代码块 GO 复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| func main() { var n, k int fmt.Scanln(&n, &k) list := make([]int, n)
in := bufio.NewReader(os.Stdin) out := bufio.NewWriter(os.Stdout)
for i := 0; i < n; i++ { fmt.Fscan(in, &list[i]) }
max, min := slidingWindow(list, k) fmt.Fprint(out, fmt.Sprint(strings.Trim(fmt.Sprint(min), "[]")+"\n"+strings.Trim(fmt.Sprint(max), "[]"))) out.Flush() return }
|
最后的运行结果是有一个点报 MLE 了(按照第二种写法,那个点是 100.16MB)
这个也比较好理解,对于数据量比较变态的情况,及时将数据输出,可以预防 MLE,所有结果合起来再输出,就有可能会超出规定内存
当然,输入也有可能会超出默认缓存大小,这个时候需要根据实际情况进行调整,例如 https://www.acwing.com/blog/content/28740/ 中提到的:
特殊场合 当一次输入超大时:3302. 表达式求值
将输入缓存设置为20000 * 1024
折叠代码块 GO 复制代码
1 2 3 4
| in = bufio.NewScanner(os.Stdin) bs = make([]byte, 20000 * 1024)
in.Buffer(bs, len(bs))
|