这个是在《go语言圣经》中的一道题,我花了大约一个小时写出了一个自己都不想看的双重循环版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
func nonSameString(s []string) []string {
for i, _ := range s {
if i+1 < len(s) && s[i] == s[i+1] {
fmt.Printf("发现%d和%d重复,开始修改", i, i+1)
for i1 := i; i1 < len(s)-1; i1++ {
s[i1] = s[i1+1]
}
s = s[:len(s)-1]
fmt.Println("原地移动,修改完成", s)
}
}
return s
}
|
如果思路不变的话,稍微优化一下嵌套也只能写成下面这个样子:
1
2
3
4
5
6
7
8
9
10
11
12
|
func nonSameString(s []string) []string {
for i, _ := range s {
if i+1 >= len(s) || s[i] != s[i+1] {
continue
}
for i1 := i; i1 < len(s)-1; i1++ {
s[i1] = s[i1+1]
}
s = s[:len(s)-1]
}
return s
}
|
但是时间复杂度O(n2)简直不能忍,再回头看书里原本删除空字符串的例子,发现其实解题的思路是完全一样的:
1
2
3
4
5
6
7
8
9
10
11
12
|
// nonempty returns a slice holding only the non-empty strings.
// The underlying array is modified during the call.
func nonempty(strings []string) []string {
i := 0
for _, s := range strings {
if s != "" {
strings[i] = s
i++
}
}
return strings[:i]
}
|
根据这个思路就可以重新写一个新的删除相同字符串的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//遍历一遍输入的字符串,把所有相邻元素中较为靠后的那一个赋值给索引i,就实现了O(n)的原地删除。
//当然,最后一个元素肯定没有能够与之比较的元素了,所以单独分配一下也ok。
func nonNearSameStr(s []string) []string {
i := 0
for in, v := range s {
if in+1 < len(s) && s[in] != s[in+1] {
s[i] = v
i++
}
}
s[i] = s[len(s)-1]
return s[:i+1]
}
|
写出的程序看起来都比之前正常多了。。
《go语言圣经》这本书里的例子都非常经典,没有一个多余的废话,值得慢慢品味。
偷偷的说一下,在谷歌搜索这个题目,第一页的好几个答案都是时间复杂度超高的解法,比如使用了copy
函数。