当前位置:网站首页>Go (一) 基础部分3 -- 数组,切片(append,copy),map,指针
Go (一) 基础部分3 -- 数组,切片(append,copy),map,指针
2022-08-03 05:11:00 【天才小楼楼】
一、数组(列表) -- 在定义是需要指定长度和数组的类型
1.1、数组的定义
数组是存放元素的容器,必须指定存放元素的类型和长度,数组的长度是数组类型的一部分。
数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。
数组定义:var数组变量名[元素数量]TYPE
比如:var a [5]int, var a [10]int,数组的长度必须是常量,并且长度是数组类型的一部分。一旦定义,长度不能变。 [5]int 和 [10]int 是不同的类型。var a [3]int
var b [4]int
a = b 这里不能做赋值操作,因为此时a和b是不同的类型。
数组可以通过下标(索引)进行访问,类似Python的列表索引取值一样。1.2、数组的初始化 (数组的赋值)
方法一:初始化数组时可以使用初始化列表来设置数组元素的值。
func main() { var testArray [3]int //数组会初始化为int类型的零值 var numArray = [3]int{1, 2} //使用指定的初始值完成初始化 var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化 fmt.Println(testArray) //[0 0 0] fmt.Println(numArray) //[1 2 0] fmt.Println(cityArray) //[北京 上海 深圳] fmt.Println(cityArray[1]) //[北京 上海 深圳] } //[0 0 0] //[1 2 0] //[北京 上海 深圳] //上海
方法二:按照上面的方法每次都要确保提供的初始值和数组长度一致,一般情况下我们可以让编译器根据初始值的个数自行推断数组的长度,例如:
func main() { var testArray [1]int var numArray = [...]int{1, 2} var cityArray = [...]string{"北京", "上海", "深圳"} fmt.Println(testArray) //[0 0 0] fmt.Println(numArray) //[1 2] fmt.Println(cityArray) //[北京 上海 深圳] // 打印数组的类型 fmt.Printf("type of numArray: %T\n", numArray) //type of numArray: [2]int fmt.Printf("type of cityArray: %T\n", cityArray) //type of cityArray: [3]string }
方法三:我们还可以使用指定索引值的方式来初始化数组,例如:
func main() { // 设置索引1的值为1,索引3的值为5 a := [...]int{1: 1, 3: 5} fmt.Println(a) // [0 1 0 5] fmt.Printf("type of a:%T\n", a) //type of a:[4]int }
1.3、数组的遍历
func main() { // 方式一:类似python的索引取值 a := [...]string{"北", "上", "广", "深"} for i := 0; i < len(a); i += 1 { println(a[i]) } //北 //上 //广 //深 // 方法二:for + range循环取值 for k, v := range a { fmt.Println(k, v) } //0 北 //1 上 //2 广 //3 深 }
1.4、多维数组
Go语言是支持多维数组的,我们这里以二维数组为例(数组中又嵌套数组)。
1、二维数组的定义
func main() { // [[北京 上海] [广州 深圳] [成都 重庆]] // [3] 表示最外层列表内值的数量 // [2] 表示内层列表内值的数量 a := [3][2]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } // 可以理解为列表内套用列表 fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]] fmt.Println(a[2]) //支持索引取值: [成都 重庆] fmt.Println(a[2][1]) //支持索引取值: 重庆 // 延伸的列表内在套用2层列表的写法 b := [3][2][2]string{ { {"aa", "bb"}, {"cc", "dd"}}, { {"AA", "BB"}, {"CC", "DD"}}, { {"11", "22"}, {"33", "44"}}, } fmt.Println(b) // [[[aa bb] [cc dd]] [[AA BB] [CC DD]] [[11 22] [33 44]]] fmt.Println(b[2]) // [[11 22] [33 44]] fmt.Println(b[2][1]) //[33 44] }
2、二维数组的遍历
func main() { a := [3][2]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } for _, v1 := range a { for _, v2 := range v1 { fmt.Println(v2) //北京 //上海 //广州 //深圳 //成都 } } }
注意: 多维数组只有第一层可以使用...来让编译器推导数组长度。例如:
//支持的写法 a := [...][2]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } //不支持多维数组的内层使用... b := [3][...]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, }
3、二维数组内通过 append() 传值
func main() { // Step 1: 创建一个空的二位数组 values := [][]int{} // Step 2: 使用 appped() 函数向空的二维数组添加两行一维数组 row1 := []int{1, 2, 3} row2 := []int{4, 5, 6} values = append(values, row1) values = append(values, row2) fmt.Println(values) // [[1 2 3] [4 5 6]] // Step 3: 显示两行数据 fmt.Println("Row 1") fmt.Println(values[0]) fmt.Println("Row 2") fmt.Println(values[1]) // Step 4: 访问二维数组的第一个元素里面的第一个值 fmt.Println("第一个元素为:") fmt.Println(values[0][0]) }
4、数组是值类型
赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。简单理解:文件B是文件A的复制,那么改变B文件的值,不会影响A文件的值
1.5、数组的练习题
1.求数组[1, 3, 5, 7, 8]所有元素的和
func main() { //求数组[1, 3, 5, 7, 8]所有元素的和 a_list := [...]int{1, 3, 5, 7, 8} sum := 0 for _, v := range a_list { sum += v } fmt.Println(sum) }
2.从数组[1, 3, 5, 7, 8]中随机找2个数字相加的和为8的数字,并列出该数字的索引
func main() { //从数组[1, 3, 5, 7, 8]中随机找2个数字相加的和为8的数字,并列出该数字的索引,答案:(0,3)和(1,2)索引的值相加都等于8。 // 算法: // 先把第1个数字列出来,依次和后4个数相加当相加的和为8时,列出该数字的索引值。 // 然后第2个和后3位依次相加,第3个和后2位依次相加,第4个和后1位依次相加 b_list := [...]int{1, 3, 5, 7, 8} // 先把第1个数字列出来 for i := 0; i < len(b_list); i += 1 { // 依次和后4个数相加 for j := i; j < len(b_list); j += 1 { //当相加的和为8时,列出该数字的索引值。 if b_list[i]+b_list[j] == 8 { fmt.Printf("(%v,%v)", i, j) } } } }
数据和切片的区别:可以通过执行 append 方法来测试,数组不能使用 append 方法。
func main() {
// a 的定义方式就是一个数组,执行append时会报错
a := [10]int{1, 2, 3}
a = append(a, 44)
fmt.Println(a)
// first argument to append must be slice; have [3]int
// b 的定义方式就是一个数组,执行append时会报错
b := [...]int{1, 2, 3}
b = append(b, 44)
fmt.Println(b)
// first argument to append must be slice; have [3]int
// c 的定义方式就是一个切片
c := []int{1, 2, 3}
c = append(c, 44)
fmt.Println(c)
// [1,2,3,44]
}
二、切片Slice (和数组类似,只是没有长度限制,是数组的抽象) -- 切片是引用类型
2.1、切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
切片是一个引用类型,它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合。2.2、声明切片类型的基本语法如下:
varname[]type # 这2个是数组 var name [5]type | var name [...]type 和切片做区分
name: 表示变量名
type: 表示切片中的元素类型2.3、切片的定义和使用:
func main() { //切片的定义 var s1 = []int{1, 2, 3} // 定义存放 int 类型的切片,并初始化 var s2 = []string{"a", "b", "c"} // 定义存放 string 类型的切片,并初始化 fmt.Println(s1) fmt.Println(s2) } // [1 2 3] // [a b c]
2.4、切片的长度和容量
切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量func main() { //切片的定义 var s1 = []int{1, 2, 3} // 定义存放 int 类型的切片,并初始化 var s2 = []string{"a", "b", "c"} // 定义存放 string 类型的切片,并初始化 fmt.Printf("长度s1:%v,容量s1:%v\n", len(s1), cap(s1)) fmt.Printf("长度s2:%v,容量s2:%v", len(s2), cap(s2)) } //长度s1:3,容量s1:3 //长度s2:3,容量s2:3
2.5、简单切片表达式 ( a[0:3] a[low : high] ) 需要满足:0 <= low <= high <= len(a)
切片的底层就是一个数组,所以我们可以基于数组通过切片表达式得到切片。简单切片表达式中切片容量(cap)为: len(a) - low
简单切片表达式中得到的切片长度(len) 为: a[0:3] 取到的值func main() { // 数组切片 a1 := [...]int{1, 2, 3, 4, 5, 6} s1 := a1[1:4] fmt.Printf("s1:%v len(s1):%v cap(s1):%v\n", s1, len(s1), cap(s1)) } // s1:[2 3 4] len(s1):3 cap(s1):5
为了方便起见,可以省略切片表达式中的任何索引。省略了low则默认为0;省略了high则默认为切片操作数的长度:
- a := [5]int{1, 2, 3, 4, 5}
- a[2:] // 等同于 a[2:len(a)]
- a[:3] // 等同于 a[0:3]
- a[:] // 等同于 a[0:len(a)]
2.6、完整切片表达式 ( 切片后在切片 a[0:3:4] a[low : high : max] )
需要满足:0 <= low <= high <= max <= cap(a),其他条件和简单切片表达式相同。
完整切片表达式中切片容量(cap)为: max - low
完整切片表达式中得到的切片长度(len) 为: a[0:3] 取到的值func main() { // 数组切片后在切片 a := [5]int{1, 2, 3, 4, 5} t := a[1:3:5] fmt.Printf("t:%v len(t):%v cap(t):%v\n", t, len(t), cap(t)) } // t:[2 3] len(t):2 cap(t):4
判断切片是否为空:请始终使用len(s) == 0来判断,而不应该使用s == nil来判断。
2.7、使用 make()函数 构造切片
我们上面都是基于数组来创建的切片,如果需要动态的创建一个切片,我们就需要使用内置的make()函数,格式如下:
- make([]T, size, cap)
- T: 切片的元素类型
- size: 切片中元素的数量
- cap: 切片的容量
举个例子:
func main() { a := make([]int, 2, 10) fmt.Println(a) //[0 0] fmt.Println(len(a)) //2 fmt.Println(cap(a)) //10 }
上面代码中a的内部存储空间已经分配了10个,但实际上只用了2个,容量并不会影响当前元素的个数,所以len(a)返回2,cap(a)则返回该切片的容量。
2.7.1、make函数构造切片时,只写一个值2时,那么len和cap的值都为2
func main() { dst := make([]int, 2) fmt.Printf("len:%v cap:%v", len(dst), cap(dst)) // len:2 cap:2 }
2.7.2、make 初始化切片后,使用append 给切片赋值
func main() { var value = make([]string, 0, 2) value = append(value, "1", "2") fmt.Println(value) } //[1 2]
2.8、切片的本质
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)
举个例子,现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[:5],相应示意图如下:切片s2 := a[3:6],相应示意图如下:
2.9、切片的赋值拷贝
下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。func main() { s1 := make([]int, 3) //[0 0 0] s2 := s1 //将s1直接赋值给s2,s1和s2共用一个底层数组 s2[0] = 100 fmt.Println(s1) //[100 0 0] fmt.Println(s2) //[100 0 0] }
2.10、切片遍历
切片的遍历方式和数组是一致的,支持索引遍历和for range遍历。
func main() { s := []int{1, 3, 5} //方法1: for i := 0; i < len(s); i++ { fmt.Println(s[i]) } //方法2: for _, value := range s { fmt.Println(value) } }
2.11、append()方法 为切片添加元素
Go语言的内建函数append()可以为切片动态添加元素。 可以一次添加一个元素,可以添加多个元素,也可以添加另一个切片中的元素(后面加…)备注: 一维数组时需要加…,二维数组时不需要加…
func main() { var s []int // 添加一个值 s = append(s, 1) // [1] fmt.Println(s) // 添加多个值 s = append(s, 2, 3, 4) // [1 2 3 4] fmt.Println(s) // 添加另外一个数组 s2 := []int{5, 6, 7} s = append(s, s2...) // [1 2 3 4 5 6 7] fmt.Println(s) }
每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值。
举个例子:
func main() { //append()添加元素和切片扩容 var num []int for i := 0; i < 10; i += 1 { num = append(num, i) fmt.Println(num) } }
输出:
[0] len:1 cap:1 ptr:0xc000014068
[0 1] len:2 cap:2 ptr:0xc000014090
[0 1 2] len:3 cap:4 ptr:0xc000016160
[0 1 2 3] len:4 cap:4 ptr:0xc000016160
[0 1 2 3 4] len:5 cap:8 ptr:0xc00001a140
[0 1 2 3 4 5] len:6 cap:8 ptr:0xc00001a140
[0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc00001a140
[0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc00001a140
[0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc000102000
[0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc000102000从上面的结果可以看出:
append()函数将元素追加到切片的最后并返回该切片。
切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍。2.12、使用append合并多个数组 (多个数组分成两两合并)
func main() { var arr1 = []int{1, 2, 3} var arr2 = []int{4, 5, 6} var arr3 = []int{7, 8, 9} // 多个数组分成两两合并 var s1 = append(append(arr1, arr2...), arr3...) fmt.Printf("s1: %v\n", s1) // s1: [1 2 4 5 7 8] fmt.Printf("len: %v cap: %v\n", len(s1), cap(s1)) // len: 6 cap: 8 }
2.12、使用append 删除索引的值
a = append(a[:i], a[j:]...) 删除切片 a 中从索引 i 至 j 位置的值 (根据切片索引的原则,取左不取右)
func main() { // a = append(a[:i], a[j:]...):删除切片 a 中从索引 i 至 j 位置的值 s := []int{1, 2, 3, 4, 5, 6, 7} s = append(s[:1], s[5:]...) fmt.Println(s) // [1 6 7] }
2.12、使用 copy()函数 复制切片
首先我们来看一个问题:
func main() { a := []int{1, 2, 3, 4, 5} b := a fmt.Println(a) //[1 2 3 4 5] fmt.Println(b) //[1 2 3 4 5] b[0] = 1000 fmt.Println(a) //[1000 2 3 4 5] fmt.Println(b) //[1000 2 3 4 5] }
由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。
Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式:copy(源,目标 []T)
- srcSlice: 数据来源切片
- destSlice: 目标切片
举个例子1:当A把值复制到C时,C的cap大小和A一样( cap(A) = cap(C) ),那么A的值可以全部复制到C。
func main() { // copy()复制切片 a := []int{1, 2, 3, 4, 5} // [1 2 3 4 5] c := make([]int, 5, 5) // [0 0 0 0 0] //使用copy()函数将切片a中的元素复制到切片c,此时a和c的值相同 copy(c, a) fmt.Println(a) //[1 2 3 4 5] fmt.Println(c) //[1 2 3 4 5] // 修改数组c的值,不会影响数组a的值。 c[0] = 1000 fmt.Println(a) //[1 2 3 4 5] fmt.Println(c) //[1000 2 3 4 5] }
举个例子2:当A把值复制到C时,C的cap大小和A不一样:
当 cap(A) > cap(C) 时:A的值只有1和2复制到C,3因为受到了C的cap限制,无法复制过去
func main() { A := []int{1, 2, 3} C := make([]int, 2) //当 cap(A) > cap(C) fmt.Printf("cap-A:%v cap-C:%v\n", cap(A), cap(C)) // cap-A:3 cap-C:2 copy(C, A) // 把A的值复制到C fmt.Printf("A:%v C:%v", A, C) // 结果:A:[1 2 3] C:[1 2] // A的值只有1和2复制到C,3因为受到了C的cap限制,无法复制过去 }
当 cap(A) < cap(C) 时:A的值可以全部复制到C,由于C的cap值比A大,所以C的多余值用0填充
func main() { A := []int{1, 2, 3} C := make([]int, 5) //当 cap(A) < cap(C) fmt.Printf("cap-A:%v cap-C:%v\n", cap(A), cap(C)) // cap-A:3 cap-C:5 copy(C, A) // 把A的值复制到C fmt.Printf("A:%v C:%v", A, C) // 结果:A:[1 2 3] C:[1 2 3 0 0] // A的值可以全部复制到C,由于C的cap值比A大,所以C的多余值用0填充 }
三、MAP (字典)
Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。
map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。3.1、map的语法定义 map[KeyType]ValueType
- KeyType: 表示键的类型
- ValueType: 表示键对应的值的类型
map类型的变量默认初始值为空,需要使用make()函数来分配内存。语法为:
make(map[KeyType]ValueType, [cap])
其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。3.2、map基本使用
1、先初始化一个map在使用,map的预估容量(键值对)为10,会自动扩容。
func main() { // 定义方式1,先初始化一个map在使用,map的预估容量(键值对)为10,会自动扩容。 szq_dict := make(map[string]string,10) szq_dict["age"] = "18" szq_dict["name"] = "sudada" fmt.Println(szq_dict) // map[age:18 name:sudada] fmt.Printf("%T\n", szq_dict) // map[string]string }
2、定一个map然后给初始值(也可以给空值)
func main() { // 定义方式2,定一个map然后给初始值(也可以给空值) sudada := map[string]string{ "age": "18", "name": "szq", } sudada["male"] = "male" fmt.Println(sudada) // map[age:18 name:szq] fmt.Printf("%T\n", sudada) // map[string]string }
3.3、map取值
判断某个键是否存在,Go语言中有个判断map中键是否存在的特殊写法,格式如下:
value, ok := map[key]func main() { szq_dict := make(map[string]string, 10) szq_dict["age"] = "18" szq_dict["name"] = "sudada" // map 定义后,通过key取值 fmt.Println(szq_dict["age"]) // 18 fmt.Println(szq_dict["name"]) // sudada // 如果key不存在时: value, ok := szq_dict["szq"] if !ok { fmt.Println("无此键") } else { fmt.Println(value) } }
3.4、map通过for循环遍历
func main() { test_dict := make(map[string]int, 10) test_dict["sudada"] = 18 test_dict["szq"] = 28 for k, v := range test_dict { fmt.Println(k, v) } } // sudada 18 // szq 28
只想遍历key的时候,可以按下面的写法:
func main() { test_dict := make(map[string]int, 10) test_dict["sudada"] = 18 test_dict["szq"] = 28 for k, _ := range test_dict { fmt.Println(k) } } // sudada // szq
3.5、使用delete()函数删除键值对
使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:
delete(map, key)
- map: 表示要删除键值对的map
- key: 表示要删除的键值对的键
示例代码如下:
func main() { test_dict := make(map[string]int, 10) test_dict["sudada"] = 18 test_dict["szq"] = 28 fmt.Println(test_dict) // 删之前:map[sudada:18 szq:28] delete(test_dict, "szq") fmt.Println(test_dict) // 删之后:map[sudada:18] }
3.6、元素为map类型的切片 (切片里面的值均为map,可以理解为列表内的值为字典)
切片里面的map在使用时,需要做初始化操作。
func main() { // 1、先初始化切片 var mapSlice = make([]map[string]string, 3) fmt.Println(mapSlice) // [map[] map[] map[]] // 2、然后对切片中的map进行初始化,如果map未做初始化会报错:panic: assignment to entry in nil map mapSlice[0] = make(map[string]string, 10) // 对切片中的map进行赋值 mapSlice[0]["name"] = "小王子" mapSlice[0]["password"] = "123456" mapSlice[0]["address"] = "沙河" fmt.Println(mapSlice) // [map[address:沙河 name:小王子 password:123456] map[] map[]] }
3.7、值为切片类型的map (map的key对应的value是切片)
map里面的切片在使用时,需要做初始化操作。
func main() { // 1、初始化map var sliceMap = make(map[string][]string, 3) fmt.Println(sliceMap) // map[] // 2、初始化切片 var value = make([]string, 0, 2) // 给切片赋值 value = append(value, "北京", "上海") // map的key赋值 sliceMap["中国"] = value fmt.Println(sliceMap) // map[中国:[北京 上海]] }
四、指针 和 Make / New
4.1、指针 (指针取值)
任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。
指针使用流程:
- 定义指针变量。
- 为指针变量赋值。
- 访问指针变量中指向地址的值。
- 在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
func main() { // & 取变量的内存地址 age := 18 age_type := &age fmt.Println(age_type) // 0xc000014068 拿到的是内存地址 fmt.Printf("%T\n", age_type) // *int 表示 int类型的指针(内存地址) // * 根据内存地址取值 m := *age_type fmt.Println(m) // 18 }
总结:取地址操作符& 和 取值操作符* 是一对互补操作符,& 取出地址,* 根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
- 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
- 指针变量的值是指针地址。
- 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
4.2、make
4.3、new
五、练习题
5.1、写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。
func main() { str := "how do you do think you about" // 将整个字符串按照" "为分隔符做分割。得到一个数组 strSlice := strings.Split(str, " ") // 方法1 count_map := make(map[string]int, 10) for _, value := range strSlice { _, ok := count_map[value] if ok { count_map[value] += 1 } else { count_map[value] = 1 } } fmt.Println(count_map) // map[about:1 do:2 how:1 think:1 you:2] // 方法2 count_map_2 := make(map[string]int, 10) for _, v := range strSlice { count_map_2[v] = count_map_2[v] + 1 // 也可以写成:count_map[v] += 1 } fmt.Println(count_map_2) // map[about:1 do:2 how:1 think:1 you:2] }
5.2、map的value 被删除索引后,值会发生 (这里还没搞懂,先记录下)
func main() { s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} m := make(map[string][]int, 10) m["q1mi"] = s fmt.Println(m) // map[q1mi:[1 2 3 4 5 6 7 8 9]] s = append(s[:1], s[4:]...) fmt.Printf("%+v\n", m["q1mi"]) // [1 5 6 7 8 9 7 8 9] }
边栏推荐
猜你喜欢
安装IIS服务(Internet信息服务(Internet Information Services,简写IIS,互联网信息服务)
Object类与常用API
力扣561. 数组拆分
Newifi路由器第三方固件玩机教程,这个路由比你想的更强大以及智能_Newifi y1刷机_smzdm
深度学习入门之GRU
idea uses @Autowired annotation to explain the reasons and solutions
高效率科研神器——小软件、大能量
Installation of Apache DolphinScheduler version 2.0.5 distributed cluster
13.< tag-动态规划和回文字串>lt.647. 回文子串 + lt.516.最长回文子序列
Power button 561. An array of split
随机推荐
NotImplementedError: file structure not yet supported
Object类与常用API
Power button 561. An array of split
【转】最小描述长度准则MDL(Minimun Description Length)
C语言简单实现扫雷小游戏
The problem that the rosbag tool plotjuggler cannot open rosbag
VSO Downloader Ultimate 5.0.1.45 中文多语免费版 在线视频下载工具
【三子棋】7.25
mysql 存储过程 动态参数 查询执行结果
用C语言来实现五子棋小游戏
Modelarts第一次培训
13.
lt.647. Palindromic substring + lt.516. Longest palindrome subsequence 初识C语言
7.24[C语言零基础 知识点总结]
嵌入式-I2C-物理电路图
编写一个函数 reverse_string(char * string)(两种方法实现)7.26
Kaggle(四)Scikit-learn
Makefile语法
-元素之和-
MySql数据库