北屋教程网

专注编程知识分享,从入门到精通的编程学习平台

Go语言基础—链表(go语言实现单链表反转)

双向链表概述

  • 双向链表结构如下图
  • 双向链表结构中元素在内存中不是紧邻空间,而是每个元素存放上一个元素和后一个元素的地址

第一个元素称为头(head)元素,前连接(前置指针域)为nil

最后一个元素称为尾(foot)元素,后连接(后置指针域)为nil

  • 双向链表的优点:

在执行新增元素表或删除元素时效率高,获取任意一个元素,可以方便地在这个元素前后插入元素

充分利用内存空间,实现内存灵活管理

可以实现正序和逆序遍历

头元素或尾元素新增或删除时效率很高

  • 双向链表的缺点:

链表增加了元素的指针域,空间开销较大

遍历时跳跃性查找内容,大量数据遍历性能低

双向链表容器List

  • 在Go语言标准库的container/list包提供了双向链表List
  • List结构体定义如下

root表示根元素

len表示链表中有多少个元素

package main

import (
   "container/list"
)

func main() {
   type List struct {
      root Element  //报错?List.Element
      len int
   }
}


  • 其中Element结构体定义如下

next表示下一个元素,使用Next()可以获取到

prev表示上一个元素,使用Prev()可以获取到

list表示元素属于哪个链表

Value表示元素的值,interface()在Go语言中表示任意类型

// Element is an element of a linked list.
type Element struct {
   // Next and previous pointers in the doubly-linked list of elements.
   // To simplify the implementation, internally a list l is implemented
   // as a ring, such that &l.root is both the next element of the last
   // list element (l.Back()) and the previous element of the first list
   // element (l.Front()).
   next, prev *Element

   // The list to which this element belongs.
   list *List

   // The value stored with this element.
   Value interface{}
}


双向链表代码演示

  • 创建链表:

直接使用container/list包下的New()新建一个空的List

package main

import (
   "container/list"  
   "fmt"
)

//上述部分在接下来的代码中省略

func main() {
   mylist := list.New()
   fmt.Println(mylist) //输出list中内容:&{{0xc000070330 0xc000070330 <nil> <nil>} 0}
   fmt.Println(mylist.Len())//输出链表中元素的个数:0
   fmt.Printf("%p", mylist) //输出地址:0xc000070330
}
  • Go语言标准库中提供了很多向双向链表中添加元素的函数
func main() {
   mylist := list.New()
   fmt.Println(mylist) //输出list中内容:&{{0xc000070330 0xc000070330 <nil> <nil>} 0}
   fmt.Println(mylist.Len())//输出链表中元素的个数:0
   fmt.Printf("%p\n", mylist) //输出地址:0xc000070330

   //添加到最后,list["a"]
   mylist.PushBack("a")
   fmt.Println(mylist) //输出:&{{0xc0000b8360 0xc0000b8360 <nil> <nil>} 1}
   //添加到最前面list["b", "a"]
   mylist.PushFront("b")
   fmt.Println(mylist) //输出:&{{0xc0000703f0 0xc000070390 <nil> <nil>} 2}
   mylist.InsertAfter("c", mylist.Front())
   fmt.Println(mylist)//输出:&{{0xc0000703f0 0xc000070390 <nil> <nil>} 3}
   mylist.InsertBefore("d", mylist.Back())
   fmt.Println(mylist)//输出:&{{0xc0000703f0 0xc000070390 <nil> <nil>} 4}
}
  • 取出链表中的元素
func main() {    
   //取出最后一个元素的值
   fmt.Println(mylist.Back().Value) //输出:a
   //取出第一个元素的值
   fmt.Println(mylist.Front().Value) //输出:b

   //只能从头向后找,或者从后向前找,获取元素内容
   n := 2 //取出第几个元素的值
   if n > 0 && n < mylist.Len() {
      for i := 0; i < n; i++ {
         fmt.Println(mylist.Front().Next().Value) //输出两行c
      }
   }

   //遍历所有值
   for e := mylist.Front(); e != nil; e = e.Next(){
      fmt.Println(e.Value)   //依次换行输出b/c/d/a
   }
}
  • 移动元素顺序
func main() {
   //移动元素的顺序
   mylist.MoveToBack(mylist.Front()) //把第一个移动到后面
   mylist.MoveToFront(mylist.Back()) //把最后一个移动到前面
   mylist.MoveAfter(mylist.Front(),mylist.Back()) //把第一个元素移动到第二个元素后面
   mylist.MoveBefore(mylist.Front(), mylist.Back()) //把第一个参数元素,移动到第二个参数
}
  • 删除元素
func main() {
   //删除元素
   fmt.Println("=============================")
   fmt.Println(mylist.Front())//&{0xc000070390 0xc000070330 0xc000070330 d}
   mylist.Remove(mylist.Front())
   fmt.Println(mylist.Front())//&{0xc000070450 0xc000070330 0xc000070330 a}
}


双向循环链表

  • 循环链表特定是没有节点的指针域为nil,通过任何一个元素都可以找到其它元素
  • 环形链表结构如下


  • 双向循环链表和双向链表区别

1、双向循环链表没有严格意义上的头元素和尾元素

2、没有元素的前连接和后连接为nil

3、一个长度为n的双向循环链表,通过某个元素向某个方向移动,在查找最多n-1次后一定会找到另一个元素


Go语言中的双向循环链表

  • 在container/ring包下结构体Ring源码如下

1、官方明确说明了Ring是循环链表的元素,又是环形链表

2、实际使用时Ring遍历就是环形链表第一个元素

type Ring struct {
   next, prev *Ring
   Value      interface{} // for use by client; untouched by this library
}
  • Go语言标准库中对container/ring包提供的API如下


双向循环链表代码演示

  • 实例化、赋值、遍历
func main() {
   //代表整个循环链表,又代表第一个元素
   r := ring.New(5)
   r.Value=0
   r.Next().Value=1
   r.Next().Next().Value=2
   //r.Next().Next().Next().Value=3
   //r.Next().Next().Next().Next().Value=4

   r.Prev().Value=1
   r.Prev().Prev().Value=3

   //循环链表有几个元素,func执行几次,i代表当前执行元素的内容
   r.Do(func(i interface{}){
      fmt.Println(i)
   })
   /*
   0
   1
   2
   3
   1
   */
}
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言