代码随想录 day8 || 344 反转字符串 541 反转字符串|| 54 替换数字
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)。
                    
                
                
            
        
浙公网安备 33010602011771号