Go语言 排序与搜索切片

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言 排序与搜索切片相关的知识,希望对你有一定的参考价值。

参考技术A Go语言标准库中提供了sort包对整型,浮点型,字符串型切片进行排序,检查一个切片是否排好序,使用二分法搜索函数在一个有序切片中搜索一个元素等功能。

关于sort包内的函数说明与使用,请查看 https://godoc.org/sort

在这里简单讲几个sort包中常用的函数

在Go语言中,对字符串的排序都是按照字节排序,也就是说在对字符串排序时是区分大小写的。

二分搜索算法
Go语言中提供了一个使用二分搜索算法的sort.Search(size,fn)方法:每次只需要比较㏒₂n个元素,其中n为切片中元素的总数。
sort.Search(size,fn)函数接受两个参数:所处理的切片的长度和一个将目标元素与有序切片的元素相比较的函数,该函数是一个闭包,如果该有序切片是升序排列,那么在判断时使用 有序切片的元素 >= 目标元素。该函数返回一个int值,表示与目标元素相同的切片元素的索引。

在切片中查找出某个与目标字符串相同的元素索引

Go 语言范围(Range)

阅读目录

Go 语言范围(Range)

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。

在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

for 循环的 range 格式可以对 slice、map、数组、字符串 等进行迭代循环。

格式如下:

for key, value := range oldMap 
    newMap[key] = value

以上代码中的 key 和 value 是可以省略。

如果只想读取 key,格式如下:

for key := range oldMap

或者这样:

for key, _ := range oldMap

如果只想读取 value,格式如下:

for _, value := range oldMap

range 关键字在 go 语言中是相当常用好用的语法糖,可以用在 for 循环中迭代 array、slice、map、channel、字符串所有涉及到遍历输出的东西。

切片迭代

nums := []int1, 2, 3
for k, v := range nums 
	fmt.Printf("key: %v , value: %v  \\n", k, v)

key: 0 , value: 1
key: 1 , value: 2
key: 2 , value: 3

map迭代起始节点就随机了

kvs := map[string]string
	"a": "a",
	"b": "b",

for k, v := range kvs 
	fmt.Printf("key: %v , value: %v  \\n", k, v)

PS E:\\go_test> go run .\\main.go
key: a , value: a
key: b , value: b
PS E:\\go_test>

字符串迭代

for k, v := range "hello" 
	//注意这里单个字符输出的是ASCII码,用 %c 代表输出字符
	fmt.Printf("key: %v , value: %c\\n", k, v)

PS E:\\go_test> go run .\\main.go
key: 0 , value: h
key: 1 , value: e
key: 2 , value: l
key: 3 , value: l
key: 4 , value: o
PS E:\\go_test>

channel

ch := make(chan int, 10)
ch <- 11
ch <- 12
// 不用的时候记得关掉,不关掉又没有另一个goroutine存在会死锁哦,可以注释掉这一句体验死锁
close(ch)

for x := range ch 
	fmt.Println(x)

PS E:\\go_test> go run .\\main.go
11
12
PS E:\\go_test>

结构体

package main

import (
	"fmt"
	"reflect"
)

type Student struct 
	name string
	age  int


func main() 
	v := reflect.ValueOf(Student"乔峰", 29)
	count := v.NumField()
	for i := 0; i < count; i++ 
		f := v.Field(i) //字段值
		switch f.Kind() 
		case reflect.String:
			fmt.Println(f.String())
		case reflect.Int:
			fmt.Println(f.Int())
		
	

PS E:\\go_test> go run .\\main.go
乔峰
29
PS E:\\go_test>
package main

import (
	"fmt"
	"reflect"
)

type LanType struct 
	s1, s2, s3 string


var language interface = LanType"Php", "Go", "Python3"

func main() 
	value := reflect.ValueOf(language)
	for i := 0; i < value.NumField(); i++ 
		fmt.Printf("字段索引 %d: %v\\n", i, value.Field(i))
	

PS E:\\go_test> go run .\\main.go
字段索引 0: Php
字段索引 1: Go
字段索引 2: Python3
PS E:\\go_test>

golang中数组与切片的区别详析

  • 声明方式不同,数组需要指定大小,而切片不用
  • 数组是值传递,切片是引用传递

Go切片和Go数组定义

Go 切片

又称动态数组,它实际是基于数组类型做的一层封装。

Go 数组

数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从 0 开始的下标索引访问元素值。

在初始化后长度是固定的,无法修改其长度。

当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数 len(array) 获取其长度。

切片与数组的区别

  • 数组的零值是元素类型的零值,切片的零值是nil;
  • 数组是固定长度,切片是可变的长度。
  • 数组是值类型,切片是引用类型。

1、 Go 中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份。

因此,在 Go 中如果将数组作为函数的参数传递的话,那效率就肯定没有传递指针高了。

2、数组的长度也是类型的一部分,这就说明 [10]int[20]int 不是同一种数据类型。

并且Go 语言中数组的长度是固定的,且不同长度的数组是不同类型,这样的限制带来不少局限性。

3、切片(slice)是一个拥有相同类型元素的可变长序列,可以方便地进行扩容和传递,实际使用时比数组更加灵活,这也正是切片存在的意义。

而且切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。

数组

var arr1 [4]int
fmt.Printf("arr1 val:%d arr1 len:%d arr1 cap:%d\\n",
	arr1, len(arr1), cap(arr1))
arr1 val:[0 0 0 0] 
arr1 len:4 
arr1 cap:4
arr := [4]int
fmt.Printf("val:%d len:%d cap:%d\\n", 
arr, len(arr), cap(arr)) 
// val:[0 0 0 0] len:4 cap:4

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
// arr[4] = 5 
// 报错:invalid array index 4
        (out of bounds for 4-element array)
        
fmt.Printf("val:%d len:%d cap:%d\\n", 
arr, len(arr), cap(arr)) 
// val:[1 2 3 4] len:4 cap:4

ss := arr[:]
ssPtr := (*reflect.SliceHeader)(unsafe.Pointer(&ss)).Data
fmt.Printf("ss val:%d len:%d cap:%d ptr:%v\\n",
	ss, len(ss), cap(ss), ssPtr)

/*
ss val:[1 2 3 4] 
len:4 
cap:4 
ptr:824633803136
*/

ss2 := arr[:]
ss2Ptr := (*reflect.SliceHeader)(unsafe.Pointer(&ss2)).Data
fmt.Printf("ss2 val:%d len:%d cap:%d ptr:%v\\n",
	ss2, len(ss2), cap(ss2), ss2Ptr)

/*
ss2 val:[1 2 3 4] 
len:4 
cap:4 
ptr:824633803136
*/

切片

注意:切片的容量可以根据元素的个数的增多自动扩容,但是不会根据元素的个数的减少自动缩容。

var s []int
if s == nil 
	fmt.Println("nil")

fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))
/*
s val:[] 
len:0 
cap:0
*/

s = append(s, 1)
fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))
/*
s val:[1] 
len:1 
cap:1
*/

s = append(s, 1)
s = append(s, 2)
fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))
/*
s val:[1 2] 
len:2 
cap:2
*/

s = append(s, 1)
s = append(s, 2)
s = append(s, 3)
fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))

/*
s val:[1 2 3] 
len:3 
cap:4
*/

代码

package main

import "fmt"

func main() 
	fmt.Println("--------------------数组声明与赋值-----------------")
	// 指定数组大小
	var a1 [5]int
	// 自动推断数组大小
	a2 := [...]int1, 2, 3
	fmt.Println("a1=", a1, "a2=", a2)

	// 按索引赋值
	a3 := [...]int2: 2, 4: 4
	fmt.Println("a3=", a3)
	// 按索引赋值
	a4 := [6]int2: 2, 4: 4
	fmt.Println("a4=", a4)

	fmt.Println("-------------------- 切片声明与初始化-----------------")
	// 定义切片
	var b1 []int
	fmt.Println("b1=", b1)

	// 初始化
	b2 := make([]int, 3, 5)
	fmt.Printf("b2=%v,len=%d,cap=%d\\n", b2, len(b2), cap(b2))

	fmt.Println("--------------------值传递与引用传递-----------------")
	a := [4]float6467.7, 89.9, 21, 78
	b := []int2, 3, 5
	fmt.Printf("变量a---地址:%p,类型:%T,数值:%v,长度:%d \\n", &a, a, a, len(a))
	fmt.Printf("变量b---地址:%p,类型:%T,数值:%v,长度:%d \\n", &b, b, b, len(b))

	c := a
	d := b
	fmt.Printf("变量c---地址:%p,类型:%T,数值:%v,长度:%d \\n", &c, c, c, len(c))
	fmt.Printf("变量d---地址:%p,类型:%T,数值:%v,长度:%d \\n", &d, d, d, len(d))

	a[1] = 200
	fmt.Println("a=", a, "c=", c)
	d[0] = 200
	fmt.Println("b=", b, "d=", d)

PS E:\\go_test> go run .\\main.go
--------------------数组声明与赋值-----------------
a1= [0 0 0 0 0] a2= [1 2 3]
a3= [0 0 2 0 4]
a4= [0 0 2 0 4 0]
-------------------- 切片声明与初始化-----------------
b1= []
b2=[0 0 0],len=3,cap=5
--------------------值传递与引用传递-----------------
变量a---地址:0xc0000141a0,类型:[4]float64,数值:[67.7 89.9 21 78],长度:4
变量b---地址:0xc000008090,类型:[]int,数值:[2 3 5],长度:3
变量c---地址:0xc000014200,类型:[4]float64,数值:[67.7 89.9 21 78],长度:4
变量d---地址:0xc0000080d8,类型:[]int,数值:[2 3 5],长度:3
a= [67.7 200 21 78] c= [67.7 89.9 21 78]
b= [200 3 5] d= [200 3 5]
PS E:\\go_test>

数组声明与赋值

// 指定数组大小
var a1 [5]int
// 自动推断数组大小
a2 := [...]int1, 2, 3
fmt.Println("a1=", a1, "a2=", a2)
// a1= [0 0 0 0 0] a2= [1 2 3]

// 按索引赋值
a3 := [...]int2: 2, 4: 4
fmt.Println("a3=", a3)
// a3= [0 0 2 0 4]

// 按索引赋值
a4 := [6]int2: 2, 4: 4
fmt.Println("a4=", a4)
// a4= [0 0 2 0 4 0]

切片声明与初始化

// 定义切片
var b1 []int
fmt.Println("b1=", b1)
// b1= []

// 初始化
b2 := make([]int, 3, 5)
fmt.Printf("b2=%v,len=%d,cap=%d\\n",
	b2, len(b2), cap(b2))
// b2=[0 0 0],len=3,cap=5

值传递与引用传递

a := [4]float6467.7, 89.9, 21, 78
fmt.Printf("变量a---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&a, a, a, len(a))
变量a---地址:0xc000014180,
类型:[4]float64,
数值:[67.7 89.9 21 78],
长度:4
b := []int2, 3, 5
fmt.Printf("变量b---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&b, b, b, len(b))
变量b---地址:0xc000008078,
类型:[]int,
数值:[2 3 5],
长度:3
b := []int2, 3, 5
a := [4]float6467.7, 89.9, 21, 78
c := a
d := b
fmt.Printf("变量c---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&c, c, c, len(c))
变量c---地址:0xc000014180,
类型:[4]float64,
数值:[67.7 89.9 21 78],
长度:4
b := []int2, 3, 5
a := [4]float6467.7, 89.9, 21, 78
// c := a
d := b
fmt.Printf("变量d---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&d, d, d, len(d))
变量d---地址:0xc000008078,
类型:[]int,
数值:[2 3 5],
长度:3
a := [4]float6467.7, 89.9, 21, 78
c := a
b := []int2, 3, 5
d := b
a[1] = 200
fmt.Println("a=", a, "c=", c)

d[0] = 200
fmt.Println("b=", b, "d=", d)
a= [67.7 200 21 78] c= [67.7 89.9 21 78]
b= [200 3 5] d= [200 3 5]

以上是关于Go语言 排序与搜索切片的主要内容,如果未能解决你的问题,请参考以下文章

go语言笔记——切片函数常见操作,增删改查和搜索排序

GO语言练习---对切片进行排序

阶段1 Go语言基础Day02 数组切片切片原理map字符串处理/字节切片排序

Go语言专题:go语言切片

Go 语言范围(Range)

Go 语言范围(Range)