赞
踩
问题:
一个养鸡场有6只鸡,他们的体重分别是3kg .5kg 1kg 3.4kg 2kg 50kg请问这六只鸡的总体重是多少?平均体重是多少?请你编写一个程序
传统方法:定义六个变量进行求值即可
问题:不利于数据的管理和维护,不够灵活,我们需要使用新的数据类型数组
数组介绍
数组可以存放多个同一类型的数据。数组也是一种数据类型,在Go中数组就是值类型
/* 一个养鸡场有6只鸡,他们的体重分别是3kg .5kg 1kg 3.4kg 2kg 50kg请问这六只鸡的总体重是多少?平均体重是多少? 请你编写一个程序 */ func main(){ //使用数组来解决问题 //1.定义一个数组 var hens [6]float64 //2.给数组的每个元素赋值操作,元素下标从0开始 hens[0] =3.0 //hens数组的第一个元素赋值 hens[1] =5.0 hens[2] =1.0 hens[3] =3.4 hens[4] =2.0 hens[5] =50.0 //3.遍历数组求出总体重 totalweight :=0.0 for i :=0;i< len(hens);i++{ totalweight += hens[i] } //4.求出总体重 //平均体重 avgweight := fmt.Sprintf("%.2f",totalweight/float64(len(hens))) fmt.Printf("鸡的总体重是:%v,平均体重是%v",totalweight,avgweight) }
对上面代码总结
1)使用数组解决问题,程序的可维护性增加
2)而且的方法代码更加清晰,也容易扩展
数组的定义
var 数组名[数组大小] 数据类型
var a[5]int
赋初值 a[0]=1 a[1]=30 ...
数组内存(重要)
func main(){
var intArr [3]int
//当我们定义完数组后,数组的各个元素有默认值0
fmt.Println(intArr)//[0 0 0]
fmt.Printf("数组的地址是:%p",&intArr)//数组的地址是:0xc0420082c0
fmt.Printf("数组首地址是:%p",&intArr[0])
数组的首地址是:0xc0420082c0
}
func main(){
var intArr [3]int//int占8个字节 如果是int32就是4个字节
//当我们定义完数组后,数组的各个元素有默认值0
fmt.Println(intArr)//[0 0 0]
fmt.Printf("数组的地址是:%p\n",&intArr)
//数组的地址是:0xc0420082c0
fmt.Printf("数组首地址是:%p,地址intArr[1]的地址是%p",&intArr[0],&intArr[1])
//数组首地址是:0xc04205c0a0,地址intArr[1]的地址是0xc04205c0a8
}
访问数组元素
数组名[下标]比如:你要使用a数组的第三个元素 a[2]
案例:
循环输入5个成绩,保存到float64数组,并输出
func arry(){
//从终端输入5个成绩,保存到float64数组,并输出
var score[5]float64
for i:=0;i<5;i++{
fmt.Printf("请输入第%v个元素的值:",i)
fmt.Scanln(&score[i])
}
// fmt.Println("score的值是",score)
//遍历数组打印
for i :=0;i< len(score);i++{
fmt.Printf("score[%v]=%v\n",i,score[i])
}
}
四种初始化数组的方式:
func main(){ // 四种初始化数组的方式 //way1 var numArr01 [3]int=[3]int{1,2,3} fmt.Println("numArr01=",numArr01) //输出结果为:numArr= [1 2 3] //way2 var numArr02 =[3]int{5,6,7} fmt.Println("numArr02=",numArr02) //输出结果为:numArr02= [5 6 7] //way3 var numArr03 =[...]int{8,9,10} //这里的[...]是规定的写法 fmt.Println("numArr03=",numArr03) //numArr03= [8 9 10] // way4 var numArr04 =[...]int{1:800,0:900,2:999} fmt.Println("numArr04=",numArr04) //numArr04= [900 800 999] //类型推导 numArr05 :=[...]string{1:"tom",0:"jfon",2:"feilipu"} fmt.Println("numArr05=",numArr05) //numArr05= [jfon tom feilipu] }
for -range结构遍历
这是Go语言一种独有的遍历,可以用来遍历访问数组元素
基本语法
for index,value := range array01{
...
}
说明
案例演示:
func main(){ //演示for -range遍历数组 heroes :=[...]string{"刘备","张飞","关羽"} fmt.Println(heroes) //for -range遍历 for i,v :=range heroes{ fmt.Printf("i=%v,v=%v",i,v)//i=0,v=刘备i=1,v=张飞i=2,v=关羽s //除此之外这样遍历也可以 fmt.Printf("heroes[%d]=%v\n",i,heroes[i]) } //不要元素的下标只要元素的值可以这样写: for _,v :=range heroes{ fmt.Printf("元素的值=%v\n",v) } } }
1)数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化
//1)数组是多个相同类型数据的组合,一个数组一旦
// 声明/定义了,其长度是固定的,不能动态变化
var arr01 [3]int
arr01[0] =1
arr01[1] =30
//arr01[2] =1.1//这里会报错类型不一致
arr01[2] =90
// arr01[3] =890//数组会发生越界,超出指定范围长度
fmt.Println(arr01)
2)var arr []int这时arr是一个slice切片
数组需要写大小 var arr[3]int这样的写法才是数组
3)数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用
4)数据创建后,如果没有赋值,有默认值
数组类型数组 默认值为0
字符串数组,默认值""
bool数组,默认值为false
//数组创建后,如果没有赋值,有默认值(0值)
//1.数值(整数系列,浮点数系列)=》0
//2.字符串==》""
//3.bool类型 ==》flase
var arr01 [3]float32
var arr02 [3]string
var arr03 [3]bool
fmt.Printf("arr01=%v arr02=%v arr03=%v",arr01,arr02,arr03)
//输出结果 arr01=[0 0 0] arr02=[ ] arr03=[false false false]
5)使用数组的步骤:1.声明数组并开辟空间 2给数组各个元素赋值 3使用数组
6)数组的下标是从0开始的
//数组的下标是从0开始
var arr04 [3]string //0-2
fmt.Println(arr04[3])// 报错,原因是数组越界
7)数组下标必须在指定范围内使用,否则报panic,数组越界比如:var arr[5]int 则下标为0~4
8)Go的数组属于值类型,在默认情况下不是值传递,因此会进行值拷贝。数组间不会相互影响
//函数
func test01(arr [3]int){
arr[0] = 88
}
main中进行调用
arr := [3]int{11,22,33}
test01(arr)
fmt.Println(arr)//输出结果仍然是:[11 22 33] 无影响
9)如想在其它函数中,去修改原来的数组,可以使用引用传递【指针方式】
//函数
func test02(arr *[3]int){
(*arr)[0] = 88
}
main中进行调用
arr := [3]int{11,22,33}
test02(&arr)
fmt.Println("main里面的arr的值是",arr)
//输出的内容是
// main里面的arr的值是 [88 22 33]
10)长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度看案例
题1 默认值拷贝 func modify(arr []int){ //编译就直接报错因为没有指定长度 arr[0] = 100 fmt.Println("modify的值arr",arr) } func main(){ var arr = [...]int{1,2,3} modify(arr) } 题2 默认值拷贝 func modify(arr [4]int){ arr[0] = 100 fmt.Println("modify的值arr",arr) } func main(){ var arr = [...]int{1,2,3} modify(arr) } 题2 默认值拷贝 func modify(arr [4]int){ arr[0] = 100 fmt.Println("modify的值arr",arr) } func main(){ var arr = [...]int{1,2,3} modify(arr) } //编译错误,长度是数据类型的一部分 题2 默认值拷贝 func modify(arr [3]int){ arr[0] = 100 fmt.Println("modify的值arr",arr) 100 2 3 } func main(){ var arr = [...]int{1,2,3} modify(arr) // 1 2 3 } //这个正确,但是不能修改成功
1)创建一个byte类型的26个元素的数组,分别放置’A’-‘Z’,使用for循环访问所有元素并打印出来,提示:字符数据运算’A’+1->‘B’
func th1(){//自己写的 //声明一个数组 var arr [26]byte arr[0]='A' for i :=1;i<26;i++{ arr[i]=arr[i-1]+1 } for a,v :=range arr{ fmt.Printf("arr[%d]=%c ",a,v) } } func the1(){//老师写的 var myChars [26]byte for i :=0; i < 26; i++ { myChars[i] ='A'+byte(i)//注意将i =>byte } for i :=0; i < 26; i++{ fmt.Printf("%c ",myChars[i]) } } func main(){ //th1() the1() }
2)请求出一个数组的最大值,并得到对应的下标
//请求出一个数组的最大值,并得到对应的下标 /* 1.声明一个数组[6]int{12,56,7,9,23,1} 2.假定第一个数为最大值,下标就为0 3,然后从第二个元素开始循环比较,如果发现有更大则交换 */ func the2(){ arr :=[6]int{12,56,7,90,23,1} var max int =arr[0] maxValIndex :=0 for i :=1;i<len(arr);i++{ if arr[i]>max{ max=arr[i] maxValIndex=i } } fmt.Printf("max=%v,index=%v",max,maxValIndex) }
3)请求出一个数组的和以及他的平均值 。for-range
//请求出一个数组的和以及平均值。for-range func suma(){ //1.声明一个数组arr :=[6]int{12,56,7,90,23,1} //2.求出sum //3.求出平均值 arr :=[6]int{12,57,7,90,23,1} sum :=0 for _,v :=range arr{ //累积求和 sum += v } fmt.Printf("数组的和是%v,数组的平均值是%.2f",sum,float64(sum)/float64(len(arr))) } func main(){ suma() }
数组的复杂使用—数组反转
要求:随机生成5个数,并将其反转打印
/* 要求:随机生成5个数,并将其反转打印 思路 1.随机生成5个数,rand.Intn()函数 2,当我们得到随机数后就放到一个数组中 int数组 3.反转打印 */ func fanzhuan(){ var intArr3 [5]int len :=len(intArr3) //先算出数组的长度,避免反复调用 //为了每次生成的随机数都不一样,我们需要给一个seed值 rand.Seed(time.Now().UnixNano()) for i := 0; i < len);i++{ intArr3[i] =rand.Intn(100) //0<=n<=100 } fmt.Println("交换前:",intArr3) //3.反转打印,交换的次数是len/2 2.倒数第一个和第一个交换倒数第二个与第二个进行交换 temp :=0 //作为一个临时变量用于交换操作 for i := 0; i < len/2;i++{ temp =intArr3[len-1 -i] //倒数第n个和第n个元素进行交换 intArr3[len-1 -i]=intArr3[i] intArr3[i] = temp } fmt.Println(intArr3) fmt.Println("交换后:",intArr3) 最后main中调用即可
为什么需要切片?
先看一个需求:我们需要一个数组用于保存学生的成绩,但是学生的个数是不确定的,请问怎么办,解决方案:使用切片
1)切片的英文slice
2)切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
3)切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度len(slice)都一样
4)切片的长度是可以变化的,因此切片是一个可以动态变化数组
5)切片定义的基本语法
var 变量名 []类型
比如: var a []int
入门案例
func main(){ //演示切片的基本使用 var intArr [5]int = [...]int{1,22,33,66,99} //声明定义一个切片 /* 1.slice 就是切片的名称 2.intArr[1:3] 表示slice 引用到intArr这个数组 3.应用inArr数组的起始下标为1终止下标为3不包含3 */ slice := intArr[1:3] fmt.Println("intArr=",intArr) // intArr= [1 22 33 66 99] fmt.Println("slice 的元素是=",slice)//slice 的元素是= [22 33] fmt.Println("slice 的元素的个数是=",len(slice))//slice 的元素的个数是= 2 fmt.Println("slice 的容量是=",cap(slice))//slice 的容量是= 4 //切片的容量是可以动态变化的 cability fmt.Printf("intArr[1]的地址=%p\n",&intArr[1]) fmt.Printf("slice[0]的地址=%p slice[0]=%v\n",&slice[0],slice[0]) slice[0]=34 //相当于*intArr[1]=34 fmt.Println("intArr=",intArr)//intArr= [1 34 33 66 99] }
2.切片在内存中的形式
为了让大家更加深入的理解切片,我们画图分析一下切片在内存中是如何布局的
这是一个非常重要的知识点
1)以前面的案例来分析切片在内存中的布局
2)切片底层的数据结构可以理解成一个结构体struct
3)输出切片和切片的引用地址
第一种方式:定义一个切片,然后让切片去引用一个已经创建好的数组像前面的案例
func main(){
//演示切片的基本使用
var intArr [5]int = [...]int{1,22,33,66,99}
//声明定义一个切片
/*
1.slice 就是切片的名称
2.intArr[1:3] 表示slice 引用到intArr这个数组
3.应用inArr数组的起始下标为1终止下标为3不包含3
*/
slice := intArr[1:3]
fmt.Println("intArr=",intArr) // intArr= [1 22 33 66 99]
fmt.Println("slice 的元素是=",slice)//slice 的元素是= [22 33]
fmt.Println("slice 的元素的个数是=",len(slice))//slice 的元素的个数是= 2
fmt.Println("slice 的容量是=",cap(slice))//slice 的容量是= 4
第二种方式:通过make来创建切片
基本语法:
var 切片名 []type = make([].len,[cap])
参数说明:type就是数据类型 len:大小 cap指定切片的容量可选
案例演示
func main(){ var slice []int =make([]int,4,10) fmt.Println(slice)//默认值为0 fmt.Println("slice len=",len(slice),"slice cap=",cap(slice)) slice[0]=100 slice[2]=100 fmt.Println(slice) } //演示切片的使用make var slice []float64 = make([]float64,5,10) slice[1] = 10 slice[3] = 20 //对于切片,必须使用make fmt.Println(slice) fmt.Println("slice的size=",len(slice)) fmt.Println("slice的cap=",cap(slice))
内部有个数组是不可见的
对上面代码的小结:(面试重点)
1)通过make方式创建切片可以指定切片的大小和容量
2)如果没有给切片的各个元素赋值,那么就会使用默认值[int,float=>0 string=>" " bool=>false]
3)通过make方式创建的切片对应的数组是由make底层维护,对外不可见,即只能通过slice方式去访问
第3中方式:定义一个切片,直接就指定具体数组,使用原理类似make方式
//第3中方式:定义一个切片,直接就指定具体数组,使用原理类似make方式
var strSlice []string = []string {"tom","jack","mary"}
fmt.Println(strSlice)
fmt.Println("strSlice的size=",len(strSlice))//3
fmt.Println("strSlice的cap=",cap(strSlice))//3
3.切片的遍历
切片的遍历和数组一样,也有两种方式‘
1)for循环常规遍历方式
案例演示
func main(){
//使用常规的for循环遍历切片
var arr [5]int=[...]int{10,20,30,40,50}
slice :=arr[1:4]//20 30 40
for i := 0;i<len(slice);i++{
fmt.Printf("slice[%v]=%v ",i,slice[i])//slice[0]=20 slice[1]=30 slice[2]=40
}
2)for-range结构遍历切片
案例演示:
//使用for -range 方式遍历切片
for i,v :=range slice{
fmt.Printf("slice[%v]=%v ",i,v)slice[0]=20 slice[1]=30 slice[2]=40
}
1)切片初始化时 var slice = arr[start index:endindex]
说明:从arr数组下标为startindex,取到下标为endindex的元素(不含 arr[endindex])
2)切片初始化时,仍然不能越界。范围在[0-len(arr)]之间,但是可以动态增长
1)var slice = arr[0:end]可以简写 var slice = arr[:end]
2)var slice = arr[start:len(arr)]可以简写:var slice = arr[start:]
3)var slice = arr[0:len(arr)]可以简写:var slice = arr[:]
cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素
切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组或者make一个空间供切片来使用
切片可以继续切片
slice2 := slice[1:2] //20
fmt.Println("slice1=",slice2) //[20]
当一个切片元素发生变化,其关联的数组和其他切片也会变化。因为切片是引用数据类型
3)用append内置函数,可以对切片进行动态追加
//用append内置函数,可以对切片进行动态追加
var slice3 []int = []int{100,200,300}
//通过append直接对slice3追加具体的元素
slice3 = append(slice3,400,500,600)
fmt.Println("slice3=",slice3)
//通过append将切片slice3追加到slice3
slice3 = append(slice3,slice3...)
fmt.Println("slice3=",slice3)
}
4)切片append操作的底层原理分析
5)切片的拷贝操作
切片使用copy内置函数完成拷贝
//切片的拷贝操作
//切片的copy内置函数完成拷贝,举例说明
var slice4 []int =[]int{1,2,3,4,5}
var slice5 = make([]int,10)
copy(slice5,slice4)//将slice4拷贝给slice5
fmt.Println("slice4=",slice4)//[1 2 3 4 5]
fmt.Println("slice5=",slice5)//[1 2 3 4 5 0 0 0 0 0]
(1)说明:copy(para1,para2):para1和para2都是切片类型
(2)按照上面的代码来看,slice4和slice5的数据空间是独立的,相互不影响,也就是说slice[0]=9999,slice5[0]仍然是1不会受到影响
思考题
var a []int =[]int{1,2,3,4,5}
var slice5 = make([]int,1)
copy(slice5,a)//ok只拷贝一个元素
fmt.Println(slice5) //[1]
上面的代码没有问题
切片式引用类型,所以在传递时,遵守引用传递机制
func main(){
var slice[]int
var arr[5]int = [...]int{1,2,3,4,5}
slice = arr[:]
var slice2 = slice
slice2[0] =10
fmt.Println("slice2",slice2) //[10,2,3,4,5]
fmt.Println("slice",slice)//[10,2,3,4,5]
fmt.Println("arr",arr) //[10,2,3,4,5]
}
func test(slice []int){
slice[0]=100
}
func main(){
var slice = []int{1,2,3,4}
fmt.Println("slice=",slice)//[1,2,3,4]
test(slice)
fmt.Println("slice=",slice)[100,2,3,4]
}
1)string底层是一个byte数组,因此string也可以进行切片处理
案例演示:
func main(){
//string底层是一个byte数组,因此string也可以进行切片处理
str:= "hello mrliu"
//使用切片获取mrliu
slice :=str[6:]
fmt.Println("slice",slice) //slice=mrliu
}
2)string和切片在内存种的形式,”abcd“画出内存示意图
3)string是不可改变的,也就是说不能通过str[0]='z’方式来修改字符串
//string底层是一个byte数组,因此string也可以进行切片处理
str:= "hello mrliu"
//使用切片获取mrliu
slice :=str[6:]
fmt.Println("slice",slice) //slice=mrliu
//string是不可改变的,也就是说不能通过str[0]='z'方式来修改字符串
// str[0] = 'z' //错误,编译不会通过string是不可变的
4)如果需要修改字符串,可以先将string->[]byte / 或者 []rune ->修改->重写转成string
//如果需要修改字符串,可以先将string->[]byte / 或者 []rune ->修改->重写转成string
"hello mrliu"=》改成"zello mrsliu"
arr1 := []byte(str)
arr1[0]='z'
str = string(arr1)
fmt.Println(str)//zello mrliu
//细节:我们转成[]byte后,可以处理英文和数字,但是没办法处理中文
//原因是 []byte字节来处理,而一个汉字是3个字节。因此就会出现erro
//解决办法是将string转成[]rune即可,因为[]rune是按照字符处理兼容汉字
arr1 := []rune(str)
arr1[0]='北' //utf-8 21271
str = string(arr1)
fmt.Println(str)//北ello mrliu
4.切片的课堂练习
说明:编写一个函数fbn(n int),要求完成
1)可以接收一个n int
2)能够将斐波那契的数列放到切片中
3)提示,斐波那契的数列形式
arr[0] =1;arr[1]=1;arr[2]=2;arr[3]=3;arr[4]=5;arr[5]=8
func fbn (n int)([]uint64){ //声明一个切片,切片大小n fbnSlice :=make([]uint64,n) //第一个数和第二数为1 fbnSlice[0]=1 fbnSlice[1]=1 //使用for循环来存放斐波那契的数列 for i := 2; i < n ;i++{ fbnSlice[i]=fbnSlice[i - 1] + fbnSlice [i - 2] } return fbnSlice } func main(){ /* 说明:编写一个函数fbn(n int),要求完成 1)可以接收一个n int 2)能够将斐波那契的数列放到切片中 3)提示,斐波那契的数列形式 arr[0] =1;arr[1]=1;arr[2]=2;arr[3]=3;arr[4]=5;arr[5]=8 思路: 1.声明一个函数fbn(n int)([]uint64) 2.编写fbn(n int)进行for循环来存放斐波那契数列 */ fnbSlice :=fbn(10) fmt.Println(fnbSlice) //[1 1 2 3 5 8 13 21 34 55] }
(2)快速入门案例
请使用二维数组输出如下图形
000000
001000
020300
000000
使用方式:先声明/定义再赋值
实现:
1)语法:
var 数组名[大小][大小]类型
var arr [2][3]int,再赋值
2)代码演示
/* 000000 001000 020300 000000 定义声明一个二维数组 */ func demo1(){ var arr [4][6]int //赋初值 arr[1][2]=1 arr[2][1]=2 arr[2][3]=3 //遍历二维数组。按照要求输出图形 for i :=0; i< 4; i ++{ for j :=0 ;j<6;j++{ fmt.Print(arr[i][j]," ") } fmt.Println() } } func main(){ demo1() }
4)二维数组在内存中的存在形式(重点)
func arrmemory(){ var arr2 [2][3]int arr2 [1][1]=10 fmt.Println(arr2) fmt.Printf("arr2[0]的地址是%p\n",&arr2[0]) //arr2[0]的地址是0xc04207a030 与arr2[1]相差24个字节 fmt.Printf("arr2[1]的地址是%p\n",&arr2[1]) //arr2[1]的地址是0xc04207a048 fmt.Printf("arr2[0][0]的地址是%p\n",&arr2[0][0]) //arr2[0][0]的地址是0xc04207a030 fmt.Printf("arr2[1][0]的地址是%p\n",&arr2[1][0]) // arr2[1][0]的地址是0xc04207a048 }
(2)使用方式2:直接初始化
1)声明 :
var 数组名 [大小][大小]类型 =[大小][大小]类型{{初值..},{初值...}}
2)赋值(有默认值,比如 int 类型就是0)
3)使用演示
func demo3 (){
var arr3 [2][3]int = [2][3]int{{1,2,3},{4,5,6}}
fmt.Println("arr3=",arr3)//arr3= [[1 2 3] [4 5 6]]
}
4)说明:二维数组在声明/定义时也应有四种写法【和一维数组类似】
var 数组名 [大小] [大小]类型 = [大小][大小]类型{{初值...},{初值...}}
var 数组名 [大小] [大小]类型 = [...][大小]类型{{初值...},{初值...}}
var 数组名 =[大小][大小]类型{{初值...},{初值...}}
var 数组名 =[...][大小]类型{{初值...},{初值...}}
(3)二维数组的遍历
1)双层for循环完成遍历
案例:
func bainli(){
//演示二维数组的遍历
var arr3 = [2][3]int{{1,2,3},{4,5,6}}
//for循环来遍历
for i :=0;i<len(arr3);i++{
for j :=0; j < len(arr3[i]); j++{
fmt.Printf("%v\t",arr3[i][j])
}
fmt.Println()
}
}
2)for-range方式完成遍历
案例演示:
// for -range遍历
for i,v := range arr3{
for j , v2 := range v{
fmt.Printf("arr3[%v][%v]=%v\t",i,j,v2)
}
fmt.Println()
}
(3)二维数组的应用案例
定义一个二维数组,用于保存三个班,每个班五名同学成绩,并求出每个班级的平均分、以及所有班级的平均分
package main import ( "fmt" ) /* 定义一个二维数组,用于保存三个班,每个班五名同学成绩, 并求出每个班级的平均分、以及所有班级的平均分 */ func classf(){ //定义一个二维数组 var scores [3][5]float64 //2循环的输入数据 for i :=0; i < len(scores); i++{ for j :=0; j < len(scores[i]);j++{ fmt.Printf("请输入第%d班的第%d个学生的成绩\n",i+1,j+1) fmt.Scanln(&scores[i][j]) } } fmt.Println(scores) //遍历输出成绩后的二维数组,统计平均分 totalSum := 0.0 //定义一个变量用于统计所有班级的分数 for i :=0; i < len(scores); i++{ sum := 0.0 //定义一个变量,用于累计各个班级的成绩 for j :=0; j < len(scores[i]);j++{ sum += scores[i][j] } totalSum += sum fmt.Printf("第%d班级的总分%v,平均分为%v\n",i+1,sum, sum/float64(len(scores[i]))) } fmt.Printf("所有班级额度总分是%v,所有班级的平均分是%v", totalSum,totalSum/15) } func main(){ classf() }
排序就是将一组数据,依指定的顺序进行排列的过程
(1)内部排序:
指将需要处理的所有数据都加载到内部存储器中进行排序。
包括(交换式排序法、选择式排序法和****插入排序法)
(2)外部排序法
数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)
交换式排序属于内部排序法,是运用数据值比较后,依判断规则对数据位置进行交换,以达到排序的目的
交换式排序又分为两种
1)冒泡排序法(Bubble sort)
2)快速排序法(Quick sort)
(1)基本思想
冒泡排序(Bubble sort0)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的排序码,若发现逆序交换,使排序码较小的元素逐渐从后部移向前部(从下标较大的单元移向下标较小的单元),就像水底下的气泡一样逐渐向上冒。
因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换,从而减少不必要的比较
(2)案例
我们将5个数字:24,69,80,57,13使用冒泡排序将其排成一个从小到大的有序数列
package main import ( "fmt" ) //冒泡排序 func BubbleSort(arr *[5]int){ fmt.Println("排序前的ar=",(*arr)) temp :=0//临时变量用来做交换的 //完成第一轮排序(外层排序) for j :=0;j < 4;j++{ if (*arr)[j] > (*arr)[j+1]{ //交换 temp = (*arr)[j] (*arr)[j]=(*arr)[j+1] (*arr)[j+1] = temp } } fmt.Println("第一次排序过后arr=",(*arr))//[24 69 57 13 80] //完成第二轮排序(外层排序) for j :=0;j < 3;j++{ if (*arr)[j] > (*arr)[j+1]{ //交换 temp = (*arr)[j] (*arr)[j]=(*arr)[j+1] (*arr)[j+1] = temp } } fmt.Println("第三次排序过后arr=",(*arr))//[24 57 13 69 80] //完成第二轮排序(外层排序) for j :=0;j < 2;j++{ if (*arr)[j] > (*arr)[j+1]{ //交换 temp = (*arr)[j] (*arr)[j]=(*arr)[j+1] (*arr)[j+1] = temp } } fmt.Println("第三次排序过后arr=",(*arr))//arr= [24 13 57 69 80] //完成第四轮排序(外层排序) for j :=0;j < 1;j++{ if (*arr)[j] > (*arr)[j+1]{ //交换 temp = (*arr)[j] (*arr)[j]=(*arr)[j+1] (*arr)[j+1] = temp } } fmt.Println("第四次排序过后arr=",(*arr))完成第二轮排序(外层排序) for j :=0;j < 2;j++{ if (*arr)[j] > (*arr)[j+1]{ //交换 temp = (*arr)[j] (*arr)[j]=(*arr)[j+1] (*arr)[j+1] = temp } } fmt.Println("第四次排序过后arr=",(*arr))//[13 24 57 69 80] } func BubbleSort2(arr *[5]int){ fmt.Println("排序前的ar=",(*arr)) temp :=0//临时变量用来做交换的 len :=len((*arr)) //完成第一轮排序(外层排序) for i :=0;i<len-1;i++{ for j :=0;j < len-i-1;j++{ if (*arr)[j] > (*arr)[j+1]{ //交换 temp = (*arr)[j] (*arr)[j]=(*arr)[j+1] (*arr)[j+1] = temp } } } fmt.Println("排序过后arr=",(*arr)) } func main(){ //定义一个数组 arr := [5]int{24,69,80,57,13} //将数组传递给一个函数,完成排序 //BubbleSort(&arr) BubbleSort2(&arr) //调用改写之后的冒泡排序 }
1)介绍
在Golang中,我们常用的查找有两种
(2) 顺序查找
(2)二分查找
2)案例演示
(1)有一个数列:白眉鹰王、金毛狮王、紫衫龙王、青翼斧王
猜数游戏:从键盘任意输入一个名称,判断数列中是否包含此名称
package main import ( "fmt" ) /* (1)有一个数列:白眉鹰王、 金毛狮王、紫衫龙王、青翼斧王 猜数游戏:从键盘任意输入一个名称, 判断数列中是否包含此名称 思路: 1.定义一个数组:白眉鹰王、金毛狮王、紫衫龙王、青翼斧王 2.从控制台接收一个名词,依次比较如果发现有就提示 */ func find1(){ names := [4]string{"白眉鹰王","金毛狮王","紫衫龙王","青翼斧王"} var heroName = " " fmt.Println("请输入要查找的人名...") fmt.Scanln(&heroName) //顺序查找第一种方式 for i := 0; i < len(names); i++{ if heroName == names[i]{ fmt.Printf("找到了%v,下标%v",heroName,i) break } else if i ==(len(names)-1){//判断当i为最后一个下标 fmt.Printf("没有找到%v",heroName) } } } //顺序查找第二种方式(推荐) func find2(){ names := [4]string{"白眉鹰王","金毛狮王","紫衫龙王","青翼斧王"} var heroName = " " fmt.Println("请输入要查找的人名...") fmt.Scanln(&heroName) index := -1 for i :=0; i < len(names); i++{ if heroName == names[i]{ index =i break } } if index != -1{ fmt.Printf("找到了%v,下标%v",heroName,index) }else{ fmt.Printf("没有找到%v",heroName) } } func main(){ // find1() find2() }
(2)请对一个有序数列进行二分查找{1,8,10,89,1000,1234}.输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示“没有这个数”会使用到递归
二分法查找的思路分析
arr = [1,8,10,89,1000,1234] 8
二分法查找的思路:比如我们要查找的数是findVal
1.arr是有一个有序数组,并且是从小到大排序
2.先找到中间的下标middle =(leftindex + rightindex)/2然后让中间的值和findval进行比较
逻辑:
2.1如果arr[middle]>findval,就应该问 leftindex----(middle -1)
2.1如果arr[middle]<findval,就应该问 middel+1----right
2.3如果Arr[middle]==findVal就找到
对上面的逻辑进行递归执行
递归退出条件
if lefetindex > rightindex
//找不到
return ..
思路---代码
func BinaryFind(arr *[6]int,leftindex,rightindex,findVal int){ //判断leftIndex是否大于rightindex if leftindex >rightindex{ fmt.Println("没有找到") return } //先找到中间的下标 middle :=(leftindex + rightindex) /2 if (*arr)[middle] > findVal{ //说明我们要查找的数,应该在 leftIndex ---middel-1之间 BinaryFind(arr,leftindex,middle -1,findVal) }else if (*arr)[middle] < findVal{ //说明我们要查找得数在middel + -----rightindex BinaryFind(arr,middle +1,rightindex,findVal) }else{ //就是当Arr[middle]==findVal的时候 //找到了 fmt.Printf("找到了下标为%v\n",middle) } } func main(){ arr := [6]int{1,8,10,89,1000,1234} BinaryFind(&arr,0,len(arr)-1,1000)//找到了下标为4 }
map是key -value数据结构,又称为字段或者关联数组。类似其他编程语言的集合,在编程中经常使用到
基本语法
var map 变量名 map[keptype]valuetype
key可以是什么类型
golang中的map,的key可以是很多种类型,比如bool,数字,string,指针,channel,还可以是只包含前面几个类型的 接口,结构体,数组
通常key为int 、string
注意:slice,map还有function不可以,因为这几个没法用 ==来判断
valuetype可以是什么类型
valuetype的类型和key基本一样
通常为:数字(整数,浮点数)string,map,struct
map声明的举例
var a map[string]string
var a map[string]int
var a map[int]string
var a map[string]map[string]string
注意:声明是不会分配内存的,初始化需要make,分配内存后才能赋值和使用
案例演示:
func main(){
//map的声明和注意事项,map是无序的数据结构
var a map[string]string
//使用、map前,需要先make,make的作用就是给map分配数据空间
a = make(map[string]string,10) //最大可以放10对
a["ao1"]="宋江"
a["ao2"]="吴用"
a["ao3"]="李逵"
a["ao4"]="林冲"
a["ao5"]="吴用"
a["ao5"]="无名" //会覆盖同key的值
fmt.Println(a)
}
对上面代码的说明:
1)map在使用前一定要make
2)map的key是不能重复,如果重复了,则以最后这个key-value为准
3)map的value是可以相同的
4)map的key-value是无序的
1)方式1
func main(){
//map的声明和注意事项,map是无序的数据结构
var a map[string]string
//使用、map前,需要先make,make的作用就是给map分配数据空间
a = make(map[string]string,10) //最大可以放10对
a["ao1"]="宋江"
a["ao2"]="吴用"
a["ao3"]="李逵"
a["ao4"]="林冲"
a["ao5"]="吴用"
a["ao5"]="无名" //会覆盖同key的值
fmt.Println(a)
}
2)方式2
//第二种方式
cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
fmt.Println(cities)//map[no1:北京 no2:天津 no3:上海]
3)方式3
//第三种方式
heroes := map[string]string{
"heroe1" : "宋江",
"heroe2" : "林冲",
}
herroes["hertoe3"] = "张顺"
fmt.Println(heroes)//map[heroe2:林冲 heroe1:宋江 heroes:张顺]
练习:演示一个key-value的value是map的案例
比如:我们要存放3个学生的信息,每个学生有name,sex,adress信息
思路:map[string]map[string]string
代码:
package main import( "fmt" ) func main(){ studentsMap := make(map[string]map[string]string) studentsMap["no1"] = make(map[string]string,3) studentsMap["no1"]["name"] = "tom" studentsMap["no1"]["sex"] = "男" studentsMap["no1"]["adrress"] = "北京" //第二个学生 studentsMap["no2"] = make(map[string]string,3) studentsMap["no2"]["name"] = "jhon" studentsMap["no2"]["sex"] = "男" studentsMap["no2"]["adrress"] = "上海" fmt.Println(studentsMap["no1"]) fmt.Println(studentsMap["no2"]) //打印结果如下 /* map[sex:男 adrress:北京 name:tom] map[name:jhon sex:男 adrress:上海] */ }
map[“key”] =value //如果key还没有,就是增加,如果key存在就是修改
func main(){
cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
fmt.Println(cities)
//因为no3这个key已经存在,因此下面的这句话就是修改
cities["no3"] = "深圳"
fmt.Println(cities)
}
说明:
delete(map,“key”),delete是一个内置函数,如果key存在,就删除该key-value.如果key不存在,不操作,但是也不会报错
func delete
func delete(m map[Type]Type1,key Type)
内建函数delete按照指定的键将元素从个映射中删除,若m为nil或无此元素,delete将不进行操作
案例演示:
func main(){ cities := make(map[string]string) cities["no1"] = "北京" cities["no2"] = "天津" cities["no3"] = "上海" fmt.Println(cities) //因为no3这个key已经存在,因此下面的这句话就是修改 cities["no3"] = "深圳" fmt.Println(cities) //演示删除 delete(cities,"no1") fmt.Println(cities)//map[no2:天津 no3:深圳]没有no1 //当delete指定的key不存在时,删除不会操作,也不会报错 delete(cities,"no5") fmt.Println(cities)map[no2:天津 no3:深圳] //如果希望一次性删除所有的key //1.遍历啊所有的key,如何逐一删除[遍历] //2.直接make一个新空间 cities = make (map[string]string)//效率较高 fmt.Println(cities) }
案例演示
cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
//演示map的查找
val, ok := cities["no1"]
if ok{
fmt.Printf("有no1的key,值为:%v\n",val)
}else{
fmt.Println("没有no1的key,不存在这个值")
}
注意:如果cities这个map中存在“no1",那么findRes就会返回true,否则返回false
4)map的遍历:
案例演示 相对复杂的map遍历,该map的value又是一个map
说明:map的遍历使用for-range的结构遍历
//使用for-range遍历map cities := make(map[string]string) cities["no1"] = "北京" cities["no2"] = "天津" cities["no3"] = "上海" for k,v := range cities { fmt.Printf("k=%v v=%v\n",k,v) } //输出结果如下 //k=no1 v=北京k=no2 v=天津k=no3 v=上海 //使用for-range遍历比较复杂的map studentsMap := make(map[string]map[string]string) studentsMap["no1"] = make(map[string]string,3) studentsMap["no1"]["name"] = "tom" studentsMap["no1"]["sex"] = "男" studentsMap["no1"]["adrress"] = "北京" //第二个学生 studentsMap["no2"] = make(map[string]string,3) studentsMap["no2"]["name"] = "jhon" studentsMap["no2"]["sex"] = "男" studentsMap["no2"]["adrress"] = "上海" for k1,v1 :=range studentsMap{ fmt.Println("k1=",k1) for k2,v2 := range v1 { fmt.Printf("\t k2=%v v2 = %v\n",k2,v2) } } /* 输出结果如下 k1= no1 k2=name v2 = tom k2=sex v2 = 男 k2=adrress v2 = 北京 k1= no2 k2=sex v2 = 男 k2=adrress v2 = 上海 k2=name v2 = jhon
map的长度
func len
func len(v Type)int
内置函数的len返回v的长度,这取决于具体类型:
数组:v中元素的数量
数组指针:*v中元素的数量(v为nil时panic)
切片\映射:v中元素的数量:若v为nil,len(v)为0
字符串:v中字节的数量
通道:通道缓存中队列(未读取)元素的数量nil,len(v)即为0
案例演示
fmt.Printf("cities有%v 对key-value \n",len(cities)) //3对
基本介绍
切片的数据类型如果是map,则我们称为silice of map ,ma切片,这样使用规则map个数就可以动态变化了
案例演示
要求:使用一个map来记录monster的信息name和age,也就是说一个monster对应一个map,并且妖怪的个数可以动态的增加=>map切片
func main(){ //演示map切片的使用 //1.声明一个map切片 var monsters []map[string]string monsters = make([]map[string]string,2)//准备放入两个妖怪 //2.增加一个妖怪的信息 if monsters[0] ==nil { monsters[0] = make(map[string]string,2) monsters[0]["name"]="牛魔王" monsters[0]["age"]="500" } if monsters[1] ==nil { monsters[1] = make(map[string]string,2) monsters[1]["name"]="玉兔精" monsters[1]["age"]="400" } //下面这个写法越界. // if monsters[2] ==nil { // monsters[2] = make(map[string]string,2) // monsters[2]["name"]="狐狸精" // monsters[2]["age"]="300" // } //这里我们需要使用到切片的append函数,可以动态的增加monster //1.先定义一个monster信息 newMonster := map[string]string{ "name" : "新的妖怪 -火云邪神", "age" : "200", } monsters = append(monsters,newMonster) fmt.Println(monsters) }
基本介绍
1)golang中没有一个专门的方法针对map的key进行排序
2)golang中的map默认是无序的,注意也不是按照添加的顺序存放的,你每次遍历,得到的输出可能也不一样
3)golang中map的排序,是先将key进行排序,然后根据key值遍历输出即可
案例演示
func main(){ //map排序 map1 :=make(map[int]int,10) map1[10] = 100 map1[1] = 13 map1[4] = 56 map1[8] = 90 fmt.Println(map1)//map[10:100 1:13 4:56 8:90] //如何按照map的key的顺序进行排序输出 //1.先将map的key放入到切片中 //2.对切片排序、 //3.遍历切片,然后按照key来输出map的值 var keys []int for k,_:=range map1{ keys = append(keys,k) } //排序 sort.Ints(keys) fmt.Println(keys) //遍历切片,然后按照key来输出map的值 for _, k := range keys{ fmt.Printf("map[%v]=%v\n",k,map1[k]) } }
1)map是引用类型,遵守引用类型传递的机制,在一个函数接收map,修改后,会直接修改原来的map[案例演示]
func main() {
//map是引用类型,
// 遵守引用类型传递的机制,在一个函数接收map,修改后,会直接修改原来的map
map1 := make(map[int]int)
map1[1] = 90
map1[2] = 88
map1[10] = 1
map1[20] = 2
modify(map1)
//观察结果如果map1[10]= 900说明map是引用类型
fmt.Println(map1) //map[10:900 20:2 1:90 2:88]
}
2)map的容量达到后,再想map增加元素,会自动扩容,并不会发生panic,也就是说map能动态的增长键值对(key -value)
3)map的value也经常使用struct类型,更适合管理复杂的数据(比前面value是一个map更好),比如value为student结构体
//定义一个学生结构体 type Stu struct { Name string Age int Address string } func main() { //map的value也经常使用struct类型,更适合管理复杂的 // 数据(比前面value是一个map更好),比如value为student结构体 //1.map的key为学生的学号是唯一的 //2.map的value为结构体,包含学生的名字,年龄,地址 students :=make(map[string]Stu, 10) //创建2个学生 stu1 := Stu{"tom",18,"北京"} stu2 := Stu{"jhon",19,"上海"} students["no1"] = stu1 students["no2"] = stu2 fmt.Println(students) //map[no1:{tom 18 北京} no2:{jhon 19 上海}] //遍历各个学生的信息 for k,v := range students { fmt.Printf("学生的编号是%v\n",k) fmt.Printf("学生的名字是%v\n",v.Name) fmt.Printf("学生的年龄是%v\n",v.Age) fmt.Printf("学生的住址是%v\n",v.Address) fmt.Println(" ") } } //输出结果如下 map[no1:{tom 18 北京} no2:{jhon 19 上海}] 学生的编号是no2 学生的名字是jhon 学生的年龄是19 学生的住址是上海 学生的编号是no1 学生的名字是tom 学生的年龄是18 学生的住址是北京 D:\myfile\GO\project\src\go_code\map\mapdetails>
8.综合练习题:
1)使用map[string]map[string]string的map类型
2)key:表示用户名,是唯一的,不可以重复
3)如果某个用户名存在,就将其密码修改"8888",如果不存在就增加这个用户信息,(包括nickname和密码pwd)
4)编写一个函数modifyUser(users map[string]map[string] string,name string)完成上述功能
package main import( "fmt" ) func modifyUsers(users map[string]map[string]string,name string){ //判断users中是否有name // v , ok := users[name] if users[name] != nil { //有这个用户 users[name]["pws"] = "8888" }else { //没有这个用户 users[name] = make(map[string]string,2) users[name]["pws"] = "8888" users[name]["nicname"] = "昵称" + name //示意 } } func main(){ users :=make(map[string]map[string]string) users["smith"] =make(map[string]string,2) users["smith"]["pwd"] = "999999" users["smith"]["nickname"] = "小花猫" modifyUsers(users,"tom") modifyUsers(users,"mary") modifyUsers(users,"smith") fmt.Println(users) //输出结果为:map[mary:map[pws:8888 nicname: //昵称mary] smith:map[nickname:小花猫 pws:8888 pwd:999999] tom:map[pws:8888 nicname:昵称tom]] }
ess string
}
func main() {
//map的value也经常使用struct类型,更适合管理复杂的
// 数据(比前面value是一个map更好),比如value为student结构体
//1.map的key为学生的学号是唯一的
//2.map的value为结构体,包含学生的名字,年龄,地址
students :=make(map[string]Stu, 10)
//创建2个学生
stu1 := Stu{“tom”,18,“北京”}
stu2 := Stu{“jhon”,19,“上海”}
students[“no1”] = stu1
students[“no2”] = stu2
fmt.Println(students) //map[no1:{tom 18 北京} no2:{jhon 19 上海}]
//遍历各个学生的信息
for k,v := range students {
fmt.Printf(“学生的编号是%v\n”,k)
fmt.Printf(“学生的名字是%v\n”,v.Name)
fmt.Printf(“学生的年龄是%v\n”,v.Age)
fmt.Printf(“学生的住址是%v\n”,v.Address)
fmt.Println(" ")
}
}
//输出结果如下
map[no1:{tom 18 北京} no2:{jhon 19 上海}]
学生的编号是no2
学生的名字是jhon
学生的年龄是19
学生的住址是上海
学生的编号是no1
学生的名字是tom
学生的年龄是18
学生的住址是北京
D:\myfile\GO\project\src\go_code\map\mapdetails>
8.综合练习题: 1)使用map[string]map[string]string的map类型 2)key:表示用户名,是唯一的,不可以重复 3)如果某个用户名存在,就将其密码修改"8888",如果不存在就增加这个用户信息,(包括nickname和密码pwd) 4)编写一个函数modifyUser(users map[string]map[string] string,name string)完成上述功能 ```go package main import( "fmt" ) func modifyUsers(users map[string]map[string]string,name string){ //判断users中是否有name // v , ok := users[name] if users[name] != nil { //有这个用户 users[name]["pws"] = "8888" }else { //没有这个用户 users[name] = make(map[string]string,2) users[name]["pws"] = "8888" users[name]["nicname"] = "昵称" + name //示意 } } func main(){ users :=make(map[string]map[string]string) users["smith"] =make(map[string]string,2) users["smith"]["pwd"] = "999999" users["smith"]["nickname"] = "小花猫" modifyUsers(users,"tom") modifyUsers(users,"mary") modifyUsers(users,"smith") fmt.Println(users) //输出结果为:map[mary:map[pws:8888 nicname: //昵称mary] smith:map[nickname:小花猫 pws:8888 pwd:999999] tom:map[pws:8888 nicname:昵称tom]] }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。