前言
本章为《Go语言圣经》第四章前半部分即4.1-4.9,主要为算法题或语法题。后半部分4.10-4.14为编程应用题,文字阐述量较大,故笔者将其分为上下两章进行分开叙述。
项目源码地址
4.1-4.2
前置知识
题目中的各种编码怎么产生?
SHA256
var a string
fmt.Scan(&a)
c1 := sha256.Sum256([]byte(a))
SHA384
var a string
fmt.Scan(&a)
c1 := sha512.Sum384([]byte(x))
SHA512
var a string
fmt.Scan(&a)
c1 := sha512.Sum512([]byte(x))
4.1
题目
编写一个函数,计算两个SHA256哈希码中不同bit的数目。(参考2.6.2节的PopCount函数。)
解析
枚举SHA256编码即可,注意SHA256编码一共有32位,每位有8byte
代码
package main
import (
"crypto/sha256"
"fmt"
)
func main() {
var a, b string
fmt.Scan(&a, &b)
c1 := sha256.Sum256([]byte(a))
c2 := sha256.Sum256([]byte(b))
fmt.Printf("out1: %x\n", c1)
fmt.Printf("out2: %x\n", c2)
cnt := 0
for i := 0; i < 32; i++ {
a, b := c1[i], c2[i]
for j := 0; j < 8; j++ {
if (a >> j & 1) != (b >> j & 1) {
cnt++
}
}
}
fmt.Println("diff:", cnt)
}
4.2
题目
编写一个程序,默认情况下打印标准输入的SHA256编码,并支持通过命令行flag定制,输出SHA384或SHA512哈希算法。
解析
语法题,根据输入flag来进行函数的使用即可
代码
package main
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
"os"
"strconv"
)
func main() {
var x string
flag := 0
for p, val := range os.Args[1:] {
if p == 1 {
x = val
} else {
flag, _ = strconv.Atoi(val)
}
}
if flag == 0 {
res := sha256.Sum256([]byte(x))
fmt.Printf("%x", res)
} else if flag == 1 {
res := sha512.Sum384([]byte(x))
fmt.Printf("%x", res)
} else {
res := sha512.Sum512([]byte(x))
fmt.Printf("%x", res)
}
}
4.3-4.7
前置知识
题目代码
// reverse reverses a slice of ints in place.
func reverse(s []int) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
4.3
题目
重写reverse函数,使用数组指针代替slice
解析
语法题,将reverse函数中的slice替换即可,注意数组要取地址
代码
package main
import "fmt"
func reverse(s *[5]int) {
l, r := 0, 4
for l < r {
s[l], s[r] = s[r], s[l]
l++
r--
}
}
func main() {
a := [5]int{1, 2, 3, 4, 5}
reverse(&a)
fmt.Println(a)
}
4.4
题目
编写一个rotate函数,通过一次循环完成左移K次
解析
算法题,一个数组向左移k次即前k%len个数移动至最后,后len-k%len个数移动至开头
代码
package main
import "fmt"
func reverse(arr []int, k int) []int {
len := len(arr)
res := make([]int, len)
p := 0
k %= len
for i := k; i < len; i++ {
res[p] = arr[i]
p++
}
for i := 0; i < k; i++ {
res[p] = arr[i]
p++
}
return res
}
func main() {
var k int
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Scan(&k)
arr = reverse(arr, k)
fmt.Println(arr)
}
4.5
题目
写一个函数在原地完成消除[]string中相邻重复的字符串的操作
解析
算法题,双指针算法轻松搞定,请参考LeetCode 80.删除有序数组中的重复项
代码
package main
import "fmt"
func main() {
var a []string
var n int
fmt.Scan(&n)
for n > 0 {
var s string
fmt.Scan(&s)
a = append(a, s)
n--
}
for i := 0; i < len(a); i++ {
j := i + 1
for j < len(a) && a[j] == a[i] {
j++
}
a = append(a[0:i+1], a[j:]...)
i = j - 1
}
fmt.Println(a)
}
4.6
题目
编写一个函数,原地将一个UTF-8编码的[]byte类型的slice中相邻的空格(参考unicode.IsSpace)替换成一个空格返回
解析
与上题类似,注意空格符号判断使用unicode.IsSpace()函数
代码
package main
import (
"fmt"
"unicode"
)
func uniqueSpace(s []byte) []byte {
for i := 0; i < len(s); i++ {
if unicode.IsSpace(rune(s[i])) {
j := i
for j < len(s) && unicode.IsSpace(rune(s[j])) {
j++
}
s = append(s[:i], s[j-1:]...)
i = j - 1
}
}
return s
}
func main() {
s := "afdigneigu \n\t fjidfiefh jsdifj frewf "
str := []byte(s)
str = uniqueSpace(str)
fmt.Println(string(str))
}
4.7
题目
修改reverse函数用于原地反转UTF-8编码的[]byte。是否可以不用分配额外的内存
解析
算法题,与4.1类似,编写一个reverse函数即可
代码
package main
import "fmt"
func reverse(s []byte) {
l, r := 0, len(s)-1
for l < r {
s[l], s[r] = s[r], s[l]
l++
r--
}
}
func main() {
a := "123456789"
s := []byte(a)
reverse(s)
fmt.Println(string(s))
}
4.8-4.9
前置知识
题目代码
charcount程序
// Charcount computes counts of Unicode characters.
package main
import (
"bufio"
"fmt"
"io"
"os"
"unicode"
"unicode/utf8"
)
func main() {
counts := make(map[rune]int) // counts of Unicode characters
var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings
invalid := 0 // count of invalid UTF-8 characters
in := bufio.NewReader(os.Stdin)
for {
r, n, err := in.ReadRune() // returns rune, nbytes, error
if err == io.EOF {
break
}
if err != nil {
fmt.Fprintf(os.Stderr, "charcount: %v\n", err)
os.Exit(1)
}
if r == unicode.ReplacementChar && n == 1 {
invalid++
continue
}
counts[r]++
utflen[n]++
}
fmt.Printf("rune\tcount\n")
for c, n := range counts {
fmt.Printf("%q\t%d\n", c, n)
}
fmt.Print("\nlen\tcount\n")
for i, n := range utflen {
if i > 0 {
fmt.Printf("%d\t%d\n", i, n)
}
}
if invalid > 0 {
fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
}
}
4.8
题目
修改charcount程序,使用unicode.IsLetter等相关的函数,统计字母、数字等Unicode中不同的字符类别
解析
笔者仅完成了统计字母,数字
统计字母函数unicode.IsLetter()
统计数字函数unicode.IsDigit()
代码
// Charcount computes counts of Unicode characters.
// cat .\test.txt | go run .\main.go
package main
import (
"bufio"
"fmt"
"io"
"os"
"unicode"
"unicode/utf8"
)
func main() {
counts := make(map[rune]int) // counts of Unicode characters
var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings
invalid := 0 // count of invalid UTF-8 characters
numberCnt, letterCnt := 0, 0
in := bufio.NewReader(os.Stdin)
for {
r, n, err := in.ReadRune() // returns rune, nbytes, error
if err == io.EOF {
break
}
if err != nil {
fmt.Fprintf(os.Stderr, "charcount: %v\n", err)
os.Exit(1)
}
if r == unicode.ReplacementChar && n == 1 {
invalid++
continue
}
if unicode.IsDigit(r) {
numberCnt++
} else if unicode.IsLetter(r) {
letterCnt++
}
counts[r]++
utflen[n]++
}
fmt.Println("Number count:", numberCnt)
fmt.Println("letter count:", letterCnt)
fmt.Printf("rune\tcount\n")
for c, n := range counts {
fmt.Printf("%q\t%d\n", c, n)
}
fmt.Print("\nlen\tcount\n")
for i, n := range utflen {
if i > 0 {
fmt.Printf("%d\t%d\n", i, n)
}
}
if invalid > 0 {
fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
}
}
4.9
题目
编写一个程序wordfreq程序,报告输入文本中每个单词出现的频率。在第一次调用Scan前先调用input.Split(bufio.ScanWords)函数,这样可以按单词而不是按行输入。
解析
此处注意使用的读入函数为bufio.NewScanner(os.Stdin)并非上一题的 bufio.NewReader(os.Stdin)
进行分割之后使用,以下代码进行遍历
for in.Scan() {
word := in.Text()
}
代码
// cat .\test.txt | go run .\main.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
hash := make(map[string]int)
in := bufio.NewScanner(os.Stdin)
in.Split(bufio.ScanWords)
for in.Scan() {
word := in.Text()
hash[word]++
}
for word, cnt := range hash {
fmt.Println(word, cnt)
}
}