一、双向链表概述
* 双向链表结构中元素在内存中不是紧邻空间,而是每个元素中存放上一个元素和后一个元素的地址
* 第一个元素称为头(head)元素,前连接(前置指针域)为nil
* 最后一个元素称为尾(foot)元素,后连接(后置指针域)为nil
* 双向链表的优点:
* 在执行新增元素或删除元素时效率高,获取任意一个元素,可以方便的在这个元素前后插入元素
* 充分利用内存空间,实现内存灵活管理
* 可实现正序和逆序遍历
* 头元素和尾元素新增或删除时效率较高
* 双向链表的缺点
* 链表增加了元素的指针域,空间开销比较大
* 遍历时跳跃性查找内容,大量数据遍历性能低
二、双向链表容器List
* 在Go语言标准库的container/list 包提供了双向链表List
* List结构体定义如下
* root表示根元素
* len表示链表中有多少个元素
type List struct {
root Element
len int
}
* 其中Element结构体定义如下
* next表示下一个元素,使用Next()可以获取到
* prev表示上一个元素,使用Prev()可以获取到
* list表示元素属于哪个链表
* Value表示元素的值,interface{}在Go语言中表示任意类型
type Element struct {
next, prev *Element
list *List
Value interface{}
}
三、操作List
* 直接使用container/list包下的New()新建一个空的List
mylist := list.New()
fmt.Println(mylist) //输出list中内容
fmt.Println(mylist.Len()) //查看链表中元素的个数
fmt.Printf("%p", mylist) //输出地址
* Go语言标准库中提供了很多向双向链表中添加元素的函数
//添加到最后,List["a"]
mylist.PushBack("a")
//添加到最前面,List["b","a"]
mylist.PushFront("b")
//向第一个元素后面添加元素,List["b","c","a"]
mylist.InsertAfter("c", mylist.Front())
//向最后一个元素前面添加元素,List["b","c","d","a"]
mylist.InsertBefore("d", mylist.Back())
* 取出链表中的元素
fmt.Println(mylist.Back().Value) //最后一个元素的值
fmt.Println(mylist.Front().Value) //第一个元素的值
//只能从头向后找,或从后往前找,获取元素内容
n := 5
var curr *list.Element
if n > 0 && n <= mylist.Len() {
if n == 1 {
curr = mylist.Front()
} else if n == mylist.Len() {
curr = mylist.Back()
} else {
curr = mylist.Front()
for i := 1; i < n; i++ {
curr = curr.Next()
}
}
} else {
fmt.Println("n的数值不对")
}
//遍历所有值
for e := mylist.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
* 删除元素
mylist.Remove(mylist.Front())
四、双向循环链表
* 循环链表的特点是没有节点的指针域为nil,通过任何一个元素都可以找到其他元素
* 双向循环链表和双向链表区别
* 双向循环链表没有严格意义上的头元素和尾元素
* 没有元素的前连接和后连接为nil
* 一个长度为n的双向循环链表,通过某个元素向某个方向移动,在查找最多n-1次后一定会找到另一个元素
* Go语言标准库中对container/ring包提供的API如下
type Ring
//实例化长度为n的环形链表
func New(n int) *Ring
//长度
func (r *Ring) Len() int
//下一个元素
func (r *Ring) Next() *Ring
//上一个元素
func (r *Ring) Prev() *Ring
//移动n次,支持负数
func (r *Ring) Move(n int) *Ring
//合并s和r
func (r *Ring) Link(s *Ring) *Ring
//删除r后面n%r.Len()元素,删除多个,当前元素前面的不删除
func (r *Ring) Unlink(n int) *Ring
//循环遍历,i是当前元素的值
func (r *Ring) Do(f func(interface{}))
五、函数
* 函数:一段代码块
* 所有的流程控制代码和业务代码都只能写在函数内部
* 为什么使用函数:
* 把一个功能提取出来作为一个函数,这样便于后期维护,结构清晰
* 函数声明的语法
* 函数声明后不会执行,必须调用后才会执行
func 函数名(参数列表) 返回值{
//函数体
}
* 函数调用的语法
返回值:=函数名(参数)
* 函数可以有参数也可以没有参数,可以有返回值也可以没有返回值
* 函数的参数可以有多个,且每个参数类型都可以不同
* 参数表示调用函数方想要给函数内部传递的值,是给函数使用的值.
* 声明函数时的参数叫做形参,调用函数时的参数叫做实参
func main() {
i:=5
s:="smallming"
show(s,i)
}
func show(name string,age int){
fmt.Println("姓名:",name,"年龄",age)
}
* 函数的返回值是给调用方返回的数据,给调用方使用的.
* 具有返回值的函数,必须要有return
* 也可以在返回值类型前面添加变量,return关键字后不写内容,表示变量是什么返回值什么
六、多返回值函数
* 在Go语言中每个函数声明时都可以定义成多返回值函数
* Go语言中所有的错误都是通过返回值返回的
* 声明多返回值函数的语法
func 函数名(参数列表) (返回值,返回值){
//函数体
}
* 调用函数的语法
变量,变量:=函数名(参数)
* 调用函数时如果不想接收返回值可以使用下划线占位
变量,_:=函数名(参数)
* 理论上函数返回值个数可以无限多个,但是一般不去定义特别多个返回值(用结构体代替多返回值)
* 函数的返回值可以不接收,表示执行函数
* 函数的返回值如果接收,用于接收返回值的变量个数与返回值个数相同
* 不想接收的使用占位符(_)占位
func main() {
//不接收函数返回值
demo()
//每个返回值都接收
a, b := demo()
fmt.Println(a, b)
//不希望接收的返回值使用下划线占位
c, _ := demo()
fmt.Println(c)
}
func demo() (string, int) {
return "smallming", 17
}
七、可变参数函数
* Go语言支持可变参数函数
* 可变参数指调用参数时,参数的个数可以是任意个
* 可变参数必须在参数列表最后的位置,在参数名和类型之间添加三个点表示可变参数函数
func 函数(参数,参数,名称 ... 类型 ){
//函数体
}
* 可变参数必须存在其他参数后面,一个函数不能有多个可变参数.
func demo(hover ... string) {
for a, b := range hover {
fmt.Println(a, b)
}
}