Go编程语言中此循环的计算复杂度是多少?
var a []int
for i := 0 ; i < n ; i++ {
a = append(a, i)
}
并append
以线性时间(重新分配内存和每个追加拷贝的一切),或在固定的时间里操作(比如在许多语言方式矢量类是implemnted)?
Go编程语言规范说,append
如果需要,内置函数会重新分配。
附加并复制切片
如果s的容量不足以容纳附加值,则append分配一个新的,足够大的切片,使其既适合现有slice元素又适合附加值。因此,返回的切片可以引用不同的基础数组。
必要时,增加目标切片的精确算法取决于实现。有关当前的gc
编译器算法,请参见growslice
Go
runtime
包slice.go
源文件中的函数。固定时间摊销。
增长量切片计算部分读取:
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}
附录
在围棋编程语言规范允许语言的实现者来实现append
的内置功能在许多方面。
例如,新的分配仅必须“足够大”。分配的数量可以是parsimonius(分配最小的必要数量),或者是慷慨的,分配的数量大于最小必需的数量,以使多次调整大小的成本最小化。Go
gc
编译器使用大量的动态数组摊销恒定时间算法。
以下代码说明了append
内置函数的两种合法实现。慷慨的常量函数实现了与Go
gc
编译器相同的摊销常量时间算法。一旦完成了初始分配,parsimonius变量函数便会重新分配并复制所有内容。Go append
函数和Go
gccgo
编译器用作控件。
package main
import "fmt"
// Generous reallocation
func constant(s []int, x ...int) []int {
if len(s)+len(x) > cap(s) {
newcap := len(s) + len(x)
m := cap(s)
if m+m < newcap {
m = newcap
} else {
for {
if len(s) < 1024 {
m += m
} else {
m += m / 4
}
if !(m < newcap) {
break
}
}
}
tmp := make([]int, len(s), m)
copy(tmp, s)
s = tmp
}
if len(s)+len(x) > cap(s) {
panic("unreachable")
}
return append(s, x...)
}
// Parsimonious reallocation
func variable(s []int, x ...int) []int {
if len(s)+len(x) > cap(s) {
tmp := make([]int, len(s), len(s)+len(x))
copy(tmp, s)
s = tmp
}
if len(s)+len(x) > cap(s) {
panic("unreachable")
}
return append(s, x...)
}
func main() {
s := []int{0, 1, 2}
x := []int{3, 4}
fmt.Println("data ", len(s), cap(s), s, len(x), cap(x), x)
a, c, v := s, s, s
for i := 0; i < 4096; i++ {
a = append(a, x...)
c = constant(c, x...)
v = variable(v, x...)
}
fmt.Println("append ", len(a), cap(a), len(x))
fmt.Println("constant", len(c), cap(c), len(x))
fmt.Println("variable", len(v), cap(v), len(x))
}
输出:
GC:
data 3 3 [0 1 2] 2 2 [3 4]
append 8195 9152 2
constant 8195 9152 2
variable 8195 8195 2
gccgo:
data 3 3 [0 1 2] 2 2 [3 4]
append 8195 9152 2
constant 8195 9152 2
variable 8195 8195 2
总而言之,取决于实现方式,一旦初始容量已满,append
内置函数可能会或可能不会在每次调用时重新分配。
参考文献:
动态数组
摊销分析
附加并复制切片
如果s的容量不足以适合附加值,请
append
分配一个新的足够大的切片,使其既适合现有slice元素又适合附加值。因此,返回的切片可以引用不同的基础数组。附加到切片规范讨论
规范(技巧和1.0.3)指出:
“如果s的容量不足以容纳其他值,则
append
分配一个新的,足够大的切片,使其既适合现有slice元素又适合附加值。因此,返回的切片可能引用了另一个基础数组。”这应该是“如果且仅当”吗?例如,如果我知道切片的容量足够长,是否可以确定不会更改基础数组?
罗伯·派克
是的,你很放心。
运行时slice.go源文件
数组,切片(和字符串):“追加”的机制
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的 copy 函数和向切片追加新元素的 append 函数。 示例 7.12 copy_append_slice.go package main import "fmt" func main() { sl_from := []int{1, 2, 3} sl_to := mak
我正在从HttpServer升级到HttpClientService,作为升级的一部分,我必须将标题从标题切换到HttpHeaders。但是由于某些原因,我的自定义标题不再被追加。我需要更新什么才能添加标题? 该方法返回一个httpHeader,但它是空的。
有一个与数组相关的问题,要求是时间复杂度为O(n),空间复杂度为O(1)。 如果我使用< code>Arrays.sort(arr),并使用< code>for循环进行一次循环,例如: } 所以循环将花费O(n)时间。我的问题是:< code>Arrays.sort()会花费更多时间吗?如果我使用< code>Arrays.sort(),这个时间复杂度还是O(n)吗?< code>Arrays.s
主要内容:时间复杂度,空间复杂度《 算法是什么》一节提到,解决一个问题的算法可能有多种,这种情况下,我们就必须对这些算法进行取舍,从中挑选出一个“最好”的。 算法本身是不分“好坏”的,所谓“最好”的算法,指的是最适合当前场景的算法。挑选算法时,主要考虑以下两方面因素: 执行效率:根据算法所编写的程序,执行时间越短,执行效率就越高; 占用的内存空间:不同算法编写出的程序,运行时占用的内存空间也不相同。如果实际场景中仅能使用少量的内
问题内容: 无法附加到吗? 我正在尝试附加到对象列表。摘录之后的片段是每当作业完成时都会调用的函数。 但是,当我尝试读取它时,我只会得到文件中的第一个。然后我得到java.io.StreamCorruptedException。 要阅读我正在使用 我不知道会出现多少个对象,因此我在阅读时没有例外。根据Google的说法,这是不可能的。我想知道是否有人知道吗? 问题答案: 这是窍门:子类Object
例如,我有点混淆这两个术语——合并排序、heapsort和插入排序的辅助空间是O(1),而合并排序、插入排序和heapsort的空间复杂度是O(n)。 所以,如果有人问我合并排序、堆排序或插入排序的空间复杂度是多少,我应该告诉他们O(1)还是O(n)? 另外,请注意,在选择排序的情况下,我已经阅读了它的空间复杂度是 O(1),这是辅助空间。 那么,使用“就地计算”的算法是否有可能,对于这些算法,我