今日概要:
1.内置函数、递归函数、闭包
2.数组与切片
3.map数据结构
4.package介绍
5.互斥锁和读写锁
一、内置函数
1.close:主要用来关闭channel
2.len:用来求长度,比如string、array、slice、map、channel
3.new:用来分配内存,主要用来分配值类型,比如int,struct返回的是指针
4.make:用来分配内存,主要用来分配引用类型,比如chan,map,slice
5.append:用来追加元素到数组、slice
6.panic和recover:用来做错误处理
new的例子:
package main import "fmt" func main() { var i int fmt.Println(i) j := new(int) //值类型分配内存地址 fmt.Println(j) //打印的是内存地址 *j = 100 fmt.Println(*j) //打印指针对应的值 } /* 0 0xc420014060 100 */
new和make的区别:
package main import "fmt" func mytest(){ a := new([]int) //创建一个容量为0的切片,a为内存地址,当前为nil不能放值 b := make([]int,10) fmt.Println(a) fmt.Println(b) //将切片插入值,a为内存地址只能操作指针 *a = make([]int,5) //通过make初始化容量为5 (*a)[0] = 100 //panic: runtime error: index out of range 因为空切片需要初始化 b[0] = 100 fmt.Println(a) fmt.Println(b) } func main() { mytest() } /* &[] [0 0 0 0 0 0 0 0 0 0] &[100 0 0 0 0] [100 0 0 0 0 0 0 0 0 0] */
内置变量append
数组里append数组可以用数组...添加
目前有用到...的有 函数的可变长参数...,append加数组... 数组设置不想设置容量的时候 var a [...]int
package main import "fmt" func main() { var a []int a = append(a,20,3,40) a = append(a,a...) //数组和数组相加可以用数组... fmt.Println(a) }
内置变量panic和recover
recover是捕获异常,可以让服务不停止,比如一些异常想报警,但是又保证服务正常运行可以用defer+recover
package main import ( "time" _ "fmt" "errors" ) func getConfig() (err error){ return errors.New("init config failed!") //创建异常 } func test(){ /* //通过recover捕获异常程序不退出 defer func(){ if err := recover(); err != nil{ fmt.Println(err) } }() b := 0 a := 100 / b //异常 fmt.Println(a) */ err := getConfig() if err != nil{ panic(err) //通过panic抛出异常与python中的raise一样 } return } func main() { for { test() time.Sleep(time.Second * 1) } }
二、递归函数
go里面的递归函数和python的很像,python支持递归1000层,go支持的远远大于python
设计原则
1.一个大问题能够分解成相似的小问题
2.定义好出口条件
通过递归求阶乘
package main import "fmt" //通过递归求阶乘 func calc(n int) int{ if n == 1{ return 1 } return calc(n-1) *n } func main() { m := calc(5) fmt.Println(m) } /* 120 */
通过递归求斐波那契
package main import "fmt" func fab(n int) int{ if n <= 1{ return 1 } return fab(n-1) + fab(n-2) } func main() { for i :=0 ; i < 10 ; i++ { fmt.Println(fab(i)) } } /* 1 1 2 3 5 8 13 21 34 55 */
三、闭包
一个函数和与其相关的引用环境组合而成的实体,相当于闭包里面的变量,所有闭包都能读取
闭包例子一:
package main import "fmt" //返回值是一个func函数 func Adder() func (int) int { //类似一个类,x为类变量,修改都生效 var x int return func(d int) int{//定义了一个匿名函数 x += d return x } } func main() { f := Adder() fmt.Println(f(1)) fmt.Println(f(20)) fmt.Println(f(100)) } /* 1 21 121 */
闭包例子二:
定义的时候传入了参数,类似于python里的实例化
package main import ( "strings" "fmt" ) type str func(string) string func makeSuffix(suffix string) str { //make的时候传入suffix return func(name string) string{ if strings.HasSuffix(name,suffix) == false{ return name + suffix } return name } } func main() { f1 := makeSuffix(".mp3") fmt.Println(f1("bgm")) f2 := makeSuffix(".mp4") fmt.Println(f2("av")) } /* bgm.mp3 av.mp4 */
四、数组与切片
数组:
1.数组:是同一种数据类型的固定长度的序列。
2.数组定义:var a [len]int,比如:var a [5]int 一旦定义,长度不能改变
3.长度是数组类型的一部分,因此,var a [5] int和var a [10]int是不同的类型
4.数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
5.访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
6.数组是值类型,因此改变副本的值,不会改变本身的值
定义6的验证:
package main import "fmt" func test2(){ var a [10]int //数组是值变量,赋值之后新的改变,老的不会改变 b := a b[0] = 100 fmt.Println(a) //[0 0 0 0 0] } func main() { test1() } /* [0 0 0 0 0] */
变成指针修改当前数据原数据也被修改
package main import "fmt" func test3(a *[5]int){ //变成指针就成为引用类型 (*a)[0] = 10 } func main() { var a [5]int test3(&a) fmt.Println(a) } /* [10 0 0 0 0] */
练习:
使用非递归的方式实现斐波那契数列,打印前100个数
package main import "fmt" func fab1(n int){ var sli []int64 //声明了一个切片 sli = make([]int64,n)//将切片设定容量 sli[0] = 1 sli[1] = 1 for i :=2 ; i < len(sli);i++{ sli[i] = sli[i-1] + sli[i-2] } for _,value := range sli{ //通过for,range遍历切片 fmt.Println(value) } } func main() { fab1(10) } /* 1 1 2 3 5 8 13 21 34 55 */
循环数组的两种方式:
package main import "fmt" func test1(){ var a [5]int //定义一个数组 fmt.Println(a) //循环数组两种方式 //方式一 for i := 0; i < len(a) ; i++{ fmt.Println(a[i]) } //方式二 for key,value := range a{ fmt.Printf("%d=[%d] ",key,value) } } func main() { test1() }
数组的初始化:
a. var age0 [5]int = [5]int{1,2,3}
b. var age1 = [5]int{1,2,3,4,5}
c. var age2 = […]int{1,2,3,4,5,6} 编译的时候自动求出来长度
d. var str = [5]string{3:”hello world”, 4:”tom”}
多维数组和多维数组遍历
a. var age [5][3]int
b. var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}} 定义了一个二维数组
例子:
package main import "fmt" //数组的初始化 func testArray(){ var a [5]int = [5]int{1,2,3,4,5} var a1 = [5]int{2,3,4,5,6} var a2 = [...]int{1,2,3} var a3 = [...]string{1:"hello",3:"world"} fmt.Println(a) fmt.Println(a1) fmt.Println(a2) fmt.Println(a3) } //多维数组 func testArray1() { var a = [2][5]int{{1,2,3,4,5},{6,7,8,9,0}} //第一个是有几个{},第二个是每个{}有几个元素 fmt.Println(a) var b = [...][5]int{{1,2,3,4,5},{6,7,8,9,0}} //一维可以加...,其他不行 fmt.Println(b) //多维数组遍历 for row,v := range a{ for col,v1 := range v{ fmt.Printf("row:%d,col:%d,val:%d ",row,col,v1) } fmt.Println() } } func main() { testArray() testArray1() } /* [1 2 3 4 5] [2 3 4 5 6] [1 2 3] [ hello world] */
切片:
1. 切片:切片是数组的一个引用,因此切片是引用类型
2. 切片的长度可以改变,因此,切片是一个可变的数组
3. 切片遍历方式和数组一样,可以用len()求长度
4.cap可以求出slice最大的容量,0 <= len(slice) <= (array),其中array是slice引用的数组
5. 切片的定义:var 变量名 []类型,比如 var str []string var arr []int
6. 切片初始化:var slice []int = arr[start:end] 包含start到end之间的元素,但不包含end
7. var slice []int = arr[0:end]可以简写为 var slice []int=arr[:end]
8. var slice []int = arr[start:len(arr)] 可以简写为 var slice[]int = arr[start:]
9. var slice []int = arr[0, len(arr)] 可以简写为 var slice[]int = arr[:]
10. 如果要切片最后一个元素去掉,可以这么写: slice = slice[:len(slice)-1]
关于容量详解:
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} Slice_a := Array_a[2:5]
上面的代码真实存储方式,cap的容量是截取空间到最后的大小
练习:
package main import "fmt" func testSlice1() { //切片是引用类型,传入函数中会改变原有值 var slice []int var arr [5]int = [5]int{1, 2, 3, 4, 5} slice = arr[:] slice = slice[1:] slice = slice[:len(slice)-1] //刨除最后一个 fmt.Println(slice) fmt.Println(len(slice)) fmt.Println(cap(slice)) slice = slice[0:1] //切片可以在切片,这样cap容量不变,len会变小 fmt.Println(len(slice)) fmt.Println(cap(slice)) } func main() { testSlice1() } /* [2 3 4] 3 4 1 4 */
切片的内存布局:
切片本质上最底层还是数组,切片只是指针指向切片第一个值的内存地址,如果切片append的容量大于数组容量,底层会重新创建一个数组cap的值是成倍增长的
append
函数会改变slice
所引用的数组的内容,从而影响到引用同一数组的其它slice
。 但当slice
中没有剩余空间(即(cap-len) == 0
)时,此时将动态分配新的数组空间。返回的slice
数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice
则不受影响。
验证切片的内存地址和原数组切片第一个值的内存地址一致:
package main import "fmt" func testArray2(){ var a = [10]int{1,2,3,4} b := a[1:3] fmt.Printf("%p ",b) //验证b切片和数组内存地址一样,证明切片就是指针 fmt.Printf("%p ",&a[1])//因为从第一个元素切的所以指针指向第一个内存地址 } func main() { testArray2() } /* 0xc42001a0f8 0xc42001a0f8 */
通过自定义切片slice模拟切片操作
package main import "fmt" type slice struct { //封装成一个结构体 ptr *[100]int len int cap int } func make1(s slice,cap int) slice{ s.ptr = new([100]int) s.cap = cap s.len = 0 return s } func modify(s slice){ s.ptr[1] = 1000 } func testSlice(){ var s1 slice s1 = make1(s1,10) s1.ptr[0] = 100 modify(s1) fmt.Println(s1.ptr) } func main() { testSlice() } /* &[100 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] */
通过make来创建切片
var slice []type = make([]type, len)
slice := make([]type, len)
slice := make([]type, len, cap)
底层实现(生成了一个cap为5的数组,将切片的指针指向数组的第一个值):
用append内置函数操作切片:
slice = append(slice, 10)
var a = []int{1,2,3}
var b = []int{4,5,6}
a = append(a, b…) 切片插入切片的时候用切片名加...
练习:
package main import "fmt" func testArray3(){ var arr [5]int = [...]int{1,2,3,4,5} s := arr[1:3] fmt.Printf("before:len[%d],cap[%d] ",len(s),cap(s)) s[1] = 100 fmt.Printf("arr=%p,slice=%p ",&arr[1],s) fmt.Println("before append",arr) s = append(s,10) s = append(s,10) s = append(s,10) fmt.Printf("after:len[%d],cap[%d] ",len(s),cap(s)) //容量翻倍增加 s = append(s,10) s = append(s,10) s[1] = 1000 fmt.Println(s) fmt.Println("after append",arr) //a的值还会变,只不过没超出之前改的还是原来的内存地址 fmt.Printf("arr=%p,slice=%p ",&arr[1],s) //切片是可变长的,底层会创建一个新的数组,将指针指向新的数组 } func main() { testArray3() } /* before:len[2],cap[4] arr=0xc42001c098,slice=0xc42001c098 before append [1 2 100 4 5] after:len[5],cap[8] [2 1000 10 10 10 10 10] after append [1 2 100 10 10] arr=0xc42001c098,slice=0xc420018080 */
for range切片遍历:
for index, val := range slice {
}
切片resize
var a = []int {1,3,4,5}
b := a[1:2]
b = b[0:3]
切片copy
package main import "fmt" func testCopy(){ var a []int = []int{1,2,3,4,5} b := make([]int,10) //[0,0,0,0,0,0,0,0,0,0] copy(b,a) //内置方法copy fmt.Println(b) } func main() { testCopy() } /* [1 2 3 4 5 0 0 0 0 0] */
string和slice
string底层就是一个byte的数组,因此,也可以进行切片操作
string的底层布局
如何改变string中的字符值?
string本身是不可变的,因此要改变string中字符,需要如下操作:
package main import "fmt" func testModifyString(){ var a string = "我hello world" b := []rune(a) //有中文的时候需要通过字符数组更改,强制转为数组 b[0] = '1' fmt.Println(string(b)) } func main() { testModifyString() }
slice的排序和查找操作:
排序操作主要都在 sort包中,导入就可以使用了
sort.Ints对整数进行排序, sort.Strings对字符串进行排序, sort.Float64s对浮点数进行排序.
sort.SearchInts(a []int, b int) 从数组a中查找b,前提是a必须有序
sort.SearchFloats(a []float64, b float64) 从数组a中查找b,前提是a必须有序
sort.SearchStrings(a []string, b string) 从数组a中查找b,前提是a必须有序
练习:
package main import ( "sort" "fmt" ) func testIntSort(){ var a = [...]int{22,33,1,3,4} //数组是不能排序的,因为他是值类型 sort.Ints(a[:]) //将传入的值变为切片 fmt.Println(a) } func testStringSort(){ var a = [...]string{"aa","ccc","bbb","ffff"} sort.Strings(a[:]) fmt.Println(a) } func testFloatSort(){ var a = [...]float64{0.38,0.02,0.01,0.44} sort.Float64s(a[:]) fmt.Println(a) } func testIntSearch(){ var a = [...]int{22,33,1,3,4} sort.Ints(a[:]) index := sort.SearchInts(a[:],22) //即便你不排序,默认也会排序,但是值会混乱 fmt.Println(index) } func main() { testIntSort() testStringSort() testFloatSort() testIntSearch() } /* [1 3 4 22 33] [aa bbb ccc ffff] [0.01 0.02 0.38 0.44] 3 */
注:从Go1.2开始slice支持了三个参数的slice,之前我们一直采用这种方式在slice或者array基础上来获取一个slice
var array [10]int slice := array[2:4]
这个例子里面slice的容量是8,新版本里面可以指定这个容量
slice = array[2:4:7] 第三位是容量
上面这个的容量就是7-2
,即5。这样这个产生的新的slice就没办法访问最后的三个元素。
如果slice是这样的形式array[:i:j]
,即第一个参数为空,默认值就是0。
五、map(类似python的dict)
key-value的数据结构,又叫字典或关联数组
a.声明
声明是不会分配内存的,初始化需要make
var map1 map[keytype]valuetype
var a map[string]string
var a map[string]int
var a map[int]string
var a map[string]map[string]string
b.map的相关操作
var a map[string]string = map[string]string{“hello”: “world”} 声明加赋值
a = make(map[string]string, 10) 初始化map
a[“hello”] = “world” 插入
val, ok := a[“hello”] 查找2个返回值,第一个是值,第二个为是否查到
for k, v := range a { 遍历map
fmt.Println(k,v)
}
delete(a, “hello”) 删除map中的key
len(a) 求map长度
例子:声明初始化和make初始化
package main import "fmt" func testMap(){ //声明初始化 var a map[string]string = map[string]string{ "asdas":"asdsdasd", } //make初始化 //a := make(map[string]string,10) a["abc"] = "cde" fmt.Println(a) } func main() { testMap() }
map是引用类型:
func modify(a map[string]int) {
a[“one”] = 134 //原来的map也会改变
}
slice里面放map
items := make([]map[int][int], 5) for I := 0; I < 5; i++ { items[i] = make(map[int][int]) }
练习:
package main import "fmt" func testMap(){ //声明初始化 var a map[string]string = map[string]string{ "asdas":"asdsdasd", } //make初始化 //a := make(map[string]string,10) a["abc"] = "cde" fmt.Println(a) } //多维数组 func testMap2(){ //因为有2层数组,需要初始化两次,才能使用,map,chan,slice都需要初始化,声明为nil a := make(map[string]map[string]string,100) a["key1"] = make(map[string]string,10) a["key1"]["key2"] = "val1" a["key1"]["key3"] = "val2" a["key1"]["key4"] = "val3" a["key1"]["key5"] = "val4" fmt.Println(a) } func modify2 (a map[string]map[string]string){ //查找 _, ok := a["zhangsan"] //固定写法,第一个为值,第二个为bool if !ok { a["zhangsan"] = make(map[string]string) //判断zhangsan是否存在,不存在就创建zhangsan与对应都map } /* 等价写法 if a["zhangsan"] == nil{ a["zhangsan"] = make(map[string]string) } */ a["zhangsan"]["nickname"] = "pangpang" a["zhangsan"]["age"] = "18" return } //多维数组做用户信息保存 func testMap3(){ a := make(map[string]map[string]string,100) modify2(a) fmt.Println(a) } func trans(a map[string]map[string]string){ //遍历map for k,v := range a{ fmt.Println(k) for k1,v1 := range v{ fmt.Println(" ",k1,v1) } } } //多维数组一些操作 func testMap4(){ //遍历map a := make(map[string]map[string]string,100) a["key1"] = make(map[string]string,10) a["key1"]["key2"] = "val1" a["key1"]["key3"] = "val2" a["key1"]["key4"] = "val3" a["key1"]["key5"] = "val4" a["key2"] = make(map[string]string,10) a["key2"]["key2"] = "val1" a["key2"]["key3"] = "val2" trans(a) //删除map中key,map要全部清空for循环,或者重新make下 delete(a,"key1") trans(a) //求长度 fmt.Println(len(a)) } func testMap5(){ var a []map[int]int a = make([]map[int]int,5) if a[0] == nil{ //默认只声明的map,或者slice都是nil a[0] = make(map[int]int) } a[0][10] = 100 fmt.Println(a) } func main() { //testMap() //testMap2() //testMap3() //testMap4() testMap5() }
map的排序
a. 先获取所有key,把key进行排序
b. 按照排序好的key,进行遍历
package main import ( "fmt" "sort" ) func testMapSort(){ //保证排序正常 var a map[int]int a = make(map[int]int,5) a[0] = 1 a[2] = 3 a[3] = 4 a[10] =100 fmt.Println(a) var keys []int for k, _ := range a{ keys = append(keys,k) //append会直接增加不需要make } sort.Ints(keys) for _,v := range keys{ fmt.Println(a[v]) } } func main() { testMapSort() }
map的反转
a. 初始化一个新map,把key、value互换即可
package main import ( "fmt" ) func testMapReverse(){ //反转要新生成一个map,需要注意字符类型 var a map[string]int a = make(map[string]int,5) var b map[int]string b = make(map[int]string,5) a["aaa"] = 1 a["bbb"] = 3 for k,v := range a{ b[v] = k } fmt.Println(b) } func main() { testMapReverse() }
六、go中的sync包
a. import(“sync”)
b. 互斥锁, var lock sync.Mutex
c. 读写锁, var rwlock sync.RWMutex
读写锁适用范围:读多写少的场景,读写锁是互斥锁性能的100多倍
在读写锁机制下,允许同时有多个读者读访问共享资源,只有写者才需要独占资源。相比互斥机制,读写机制由于允许多个读者同时读访问共享资源,进一步提高了多线程的并发度。
go build --race 可以检查代码里是不是有重复修改数据的点
互斥锁例子:
package main import ( "fmt" "math/rand" "sync" "time" ) var lock sync.Mutex //互斥锁 func testMutx(){ //go build --race 查看有没有竞争关系,编译后执行返回 //互斥锁同一时间只有一个执行 var a map[int]int a = make(map[int]int,100) a[0] = 1 a[1] = 2 a[2] = 3 a[3] = 4 for i := 0; i < 50 ; i++{ go func(b map[int]int){ lock.Lock() //多个goroutine同时修改同一份数据需要互斥锁 b[0] = rand.Intn(100) lock.Unlock() }(a) } lock.Lock() fmt.Println(a) lock.Unlock() time.Sleep(time.Second * 1) } func main() { testMutx() }
读写锁例子:
sync/atomic 这个方法保证是原子操作(串行)
package main import ( "sync" //"math/rand" "fmt" "sync/atomic" "time" ) var lock sync.Mutex var rwlock sync.RWMutex //读写锁适用于读多写少的场景 //设置3秒,读写锁执行22万次,互斥锁执行2292次 //go get xx下载第三方包,默认会下载到gopath的src下 func testRwlock(){ var a map[int]int a = make(map[int]int) var count int32 a[0] = 10 a[1] = 10 a[2] = 10 a[3] = 10 a[4] = 10 for i := 0; i < 2 ; i++{ go func(b map[int]int){ //lock.Lock() rwlock.Lock() //b[0] = rand.Intn(100) time.Sleep(3 * time.Millisecond) rwlock.Unlock() lock.Unlock() }(a) } for i := 0; i < 100 ; i++{ go func(b map[int]int){ //lock.Lock() for { //lock.Lock() rwlock.RLock() //fmt.Println(a) time.Sleep(time.Millisecond) rwlock.RUnlock() //lock.Unlock() atomic.AddInt32(&count,1) //atomic原子操作,相当于串行 } }(a) } time.Sleep(time.Second * 3) fmt.Println(atomic.LoadInt32(&count)) //原子操作 } func main() { testRwlock() }
练习
实现一个冒泡排序:
冒泡排序定义:
package main import "fmt" //切片是变长,数组是固定长度 //冒泡循环两层循环,第二个每次都从后往前确定一个值 func bsSort(a []int){ for i := 0;i < len(a);i++{ //每个元素执行一个冒泡 for j := 1; j < len(a) - i;j++{ if (a[j] < a[j-1]){ //当前值 小于 上一个值就交换位置 a[j],a[j-1] = a[j-1],a[j] } fmt.Println(a) } } } func main() { slice := []int{8,7,5,4,3,10,15} //切片 bsSort(slice) fmt.Println("finally: ",slice) } /* [7 8 5 4 3 10 15] [7 5 8 4 3 10 15] [7 5 4 8 3 10 15] [7 5 4 3 8 10 15] [7 5 4 3 8 10 15] [7 5 4 3 8 10 15] [5 7 4 3 8 10 15] [5 4 7 3 8 10 15] [5 4 3 7 8 10 15] [5 4 3 7 8 10 15] [5 4 3 7 8 10 15] [4 5 3 7 8 10 15] [4 3 5 7 8 10 15] [4 3 5 7 8 10 15] [4 3 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] finally: [3 4 5 7 8 10 15] */
python实现一个冒泡排序:
L = [3, 5, 6, 7, 8, 1, 2] for i in range(len(L)-1): for j in range(len(L)-1-i): if L[j] >L[j+1]: L[j], L[j+1] = L[j+1], L[j] print(L)
实现一个选择排序(从小到大)
package main import "fmt" //选择排序 func selectSort(a []int){
//先选出最小的放到最左面,在一次选择 for i := 0;i < len(a);i++{ //循环整个序列 var min int = i for j:= i + 1; j < len(a);j++{ //循环从1:最后的序列 if (a[min] > a[j]){ //a[min] > a[j] m为当前值 大于下一个值 min = j } fmt.Println(a) } a[i],a[min] = a[min],a[i] //选出来的最小值和当前值交换 } } func main() { slice := []int{8,7,5,4,3,10,15} //切片 selectSort(slice) fmt.Println("finally: ",slice) } /* [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] finally: [3 4 5 7 8 10 15] */
实现一个插入排序(从小到大)
package main import "fmt" //插入排序 从一个有序的列表里把一个元素插入进去 func insertSort(a []int){ //一个数组是有序的,另外一个数组是无序的,无序的要去有序里比大小 for i := 1;i < len(a);i++{ //这是遍历无序的列表 for j := i; j > 0 ;j--{ //这是遍历有序的列表 进行比较插入,无序列表越来越少 ,有序的列表是插入形式,依次增加 if (a[j] > a[j-1]){ //当前值 大于下一个值就终止 break } a[j],a[j-1] = a[j-1],a[j] fmt.Println(a) } } } func main() { slice := []int{8,7,5,4,3,10,15} //切片 insertSort(slice) fmt.Println("finally: ",slice) } /* 7 8 5 4 3 10 15] [7 5 8 4 3 10 15] [5 7 8 4 3 10 15] [5 7 4 8 3 10 15] [5 4 7 8 3 10 15] [4 5 7 8 3 10 15] [4 5 7 3 8 10 15] [4 5 3 7 8 10 15] [4 3 5 7 8 10 15] [3 4 5 7 8 10 15] finally: [3 4 5 7 8 10 15] */
实现一个快速排序(从小到大)
package main import "fmt" //快速排序 func qSort(a []int,left,right int){ //选中一个值左面的比他小,右面的比他大 if left >= right{ //判断传入是否合法 return } val := a[left] k := left for i := left + 1 ; i <= right;i++{ //循环当前序列,即使被切割也是切割序列 if a[i] < val{//val是中间值,如果比val小就要放到val的左面 a[k] = a[i] //将小的数与中间值的位置交换 a[i] = a[k+1]//在将小的位置,与k的下一位交换 a[k+1] = val //将k+1的位置赋值给中间值 k++ //最终更换了一个位置所以要+1 } } //a[k] = val qSort(a,left,k-1) //在左面再次排序 qSort(a,k+1,right)//右面再次排序递归 } func main() { slice := []int{8,7,5,4,3,10,15,20,2} //切片 qSort(slice,0,len(slice)-1) fmt.Println("finally: ",slice) } /* finally: [2 3 4 5 7 8 10 15 20] */
补充:
1、有些时候需要进行多个赋值操作,由于Go里面没有,
操作符,那么可以使用平行赋值i, j = i+1, j-1
2、关于Slice定义:
当切片的索引范围大于len() 但是小于cap()的值,会读取原数组的 隐藏值进行填充
package main import "fmt" func printSlice(v []int){ fmt.Printf("slice is %v , len=(%d),cap=(%d)",v,len(v),cap(v) ) } func main(){ a := []int{1,2,3,4,5,6,7,8,9} printSlice(a) b := a[2:6] printSlice(b) fmt.Println("before slice is ",b) c := b[4:6] printSlice(c) fmt.Println("after slice is ",c) } """ slice is [1 2 3 4 5 6 7 8 9] , len=(9),cap=(9) slice is [3 4 5 6] , len=(4),cap=(7) before slice is [3 4 5 6] slice is [7 8] , len=(2),cap=(3) after slice is [7 8] 正常应该认为是7 但是有隐藏值 8,9,10 """