344 反转字符串
func reverseString(s []byte) {
// 思路 思考双指针
left , right := 0, len(s) - 1
for left < right {
s[left], s[right] = s[right], s[left]
left++
right--
}
}
//ez 没啥好说的
时间 n/2=n 空间1
541 反转字符串||
func reverseStr(s string, k int) string {
// 思路快慢指针,慢指针每次移动k,快指针每次移动2k,然后交换0-k的字符
// 通过索引取字符只能单字节,如果是多字节字符(中文),会产生问题
if len(s) <= k {
return reverse(s)
}
if len(s) > k && len(s) <= 2*k {
return reverse(s[:k]) + s[k:]
}
// 快指针遍历
start := 0
for j := 2*k; j < len(s); j += 2*k {
mid := (j + start) / 2 // 本次2k 区间的 中点
s = s[:start] + reverse(s[start:mid]) + s[mid:] // 交换前k个字符
start = j // 新的起始点
}
if len(s) - start <= k {
return s[:start] + reverse(s[start:])
}else if len(s) - start > k && len(s) - start <= 2*k {
mid := start + k
return s[:start] + reverse(s[start:mid]) + s[mid:]
}
return s
}
func reverse(s string) string {
sli := []byte(s)
left, right := 0, len(sli) - 1
for left < right {
sli[left], sli[right] = sli[right], sli[left]
left++
right--
}
return string(sli)
}
时间 反转操作k * 遍历次数 n/2k = n 空间 反转操作创建长度为k切片,字符串拼接操作创建长度为n的字符串 所以最终是n
// 在g语言中,字符串是不可变的,这意味着每次对字符串进行修改操作(如拼接)时,都会创建一个新的字符串,并分配新的内存地址。简而言之,字符串拼接操作会开辟新的内存地址。
// 思考,为什么我最开始这样写反转字符串函数是错误的呢?
func reverse(s string) string {
left, right := 0, len(s)-1
for left < right {
s[left], s[right] = s[right], s[left]
left++
right--
}
return s
}
// 原因在于,字符串本质上是一个结构体
type string struct {
data *byte
len int
}
// data存放指向底层数组的指针,len存储长度,并且对于字符串的字符而言,存储空间是连续的
// 字符串被程序设计为不可变的,所以字符串本身可以被索引,但是不能通过上面s[left], s[right] = s[right], s[left] 的方式对字符串产生修改
Go 中字符串拼接的几种方式及复杂度
1. 使用 +
操作符
s1 := "Hello"
s2 := "World"
s := s1 + s2
- 复杂度:O(n)
- 每次使用
+
拼接字符串时,都会创建一个新的字符串,并复制原有字符串的内容,这会导致时间复杂度和空间复杂度都是 O(n)。
- 每次使用
2. 使用 fmt.Sprintf
import "fmt"
s1 := "Hello"
s2 := "World"
s := fmt.Sprintf("%s%s", s1, s2)
- 复杂度:O(n)
fmt.Sprintf
会根据格式化字符串创建一个新的字符串,并复制内容,复杂度同样是 O(n)。
3. 使用 strings.Join
import "strings"
parts := []string{"Hello", "World"}
s := strings.Join(parts, "")
- 复杂度:O(n)
strings.Join
会先计算所有字符串的总长度,然后一次性分配足够的内存来存储结果字符串,因此效率较高,复杂度为 O(n)。
4. 使用 bytes.Buffer
import (
"bytes"
)
var buffer bytes.Buffer
buffer.WriteString("Hello")
buffer.WriteString("World")
s := buffer.String()
- 复杂度:O(n)
bytes.Buffer
使用一个可变大小的缓冲区来高效地拼接字符串,不会频繁地分配内存,复杂度为 O(n)。
5. 使用 strings.Builder
import (
"strings"
)
var builder strings.Builder
builder.WriteString("Hello")
builder.WriteString("World")
s := builder.String()
- 复杂度:O(n)
strings.Builder
是专门为字符串拼接设计的,它的实现类似于bytes.Buffer
,但针对字符串进行了优化,复杂度为 O(n)。
总结
方法 | 代码示例 | 复杂度 |
---|---|---|
+ 操作符 |
s := s1 + s2 |
O(n) |
fmt.Sprintf |
s := fmt.Sprintf("%s%s", s1, s2) |
O(n) |
strings.Join |
s := strings.Join(parts, "") |
O(n) |
bytes.Buffer |
buffer.WriteString("Hello") |
O(n) |
strings.Builder |
builder.WriteString("Hello") |
O(n) |
在需要频繁拼接字符串的场景下,推荐使用 strings.Builder
或 bytes.Buffer
,它们的性能相对较高,且复杂度为 O(n)。