Go-如何优雅的实现单链表?(含全部代码)

目录

简介

思路

节点结构体

属性

方法

单链表结构体

属性

方法

Init

New

Clear

Len

Front

Back

insert

InsertAfter

PushBack

PushFront

remove

RemoveAfter

RemoveFront

测试

常见问题

怎么遍历呀?

全部代码


简介

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成是元素加指针。

思路

为了能够兼容所有类型,采用空接口。为了更好的封装,用"私有"的next和insert、remove等,看下方详细解释。

节点结构体

属性

type ListNode struct {
    Val interface{}
    next *ListNode
    lst *SingleList
}
  • Val:节点的值
  • next:下一节点指针
  • lst:所属单链表

Val采用空接口,接收任意数据类型,可通过类型断言再赋给对应类型。

方法

返回下一个节点,O(1)

func (node *ListNode)Next() *ListNode {
	return node.next
}

主要用于遍历,我在其他方法中也有调用。

单链表结构体

属性

type SingleList struct {
	head *ListNode
	len int
}
  • head:头结点
  • len:单链表长度

这里其实可以将head的Val用来存储长度,可以稍微节省一点内存,由于还需要类型断言,比较麻烦,就算了

方法

Init

func (lst *SingleList)Init(){
	lst.head = new(ListNode)
	lst.head.lst = lst
	lst.len = 0
}

创建头结点,长度为0,O(1)

New

func New() *SingleList {
	lst := new(SingleList)
	lst.Init()
	return lst
}

返回创建的单链表,调用了Init,O(1)

Clear

func (lst *SingleList)Clear(){
	lst.Init()
}

调用Init,利用Go自己的垃圾回收,O(1)

Len

func (lst *SingleList)Len() int {
	return lst.len
}

返回单链表长度,O(1)

Front

func (lst *SingleList)Front() *ListNode {
	return lst.head.Next()
}

返回第一个节点,就是头结点的下一个节点,空链表情况下为nil,O(1)

Back

func (lst *SingleList)Back() *ListNode {
	if lst.len == 0{
		return nil
	}
	cur := lst.head.Next()
	for{
		if cur.Next() == nil{
			return cur
		}
		cur = cur.Next()
	}
}

返回最后一个节点,空链表的情况下为nil,O(n)

insert

func (lst *SingleList)insert(e, at *ListNode) *ListNode{
	lst.len++
	e.next = at.next
	at.next = e
	e.lst = lst
	return e
}

返回指向插入的节点e的指针。

  • e:待插入节点
  • at:链表

仅在此函数进行长度增加,其他插入函数调用此函数,O(1)。

InsertAfter

func (lst *SingleList)InsertAfter(val interface{}, mark *ListNode) *ListNode {
	if mark == nil{
		return nil
	}
	if mark.lst == lst{
		return lst.insert(&ListNode{Val:val},mark)
	}
	return nil
}

生成值为val的节点,并插入链表节点mark后,返回插入的节点,mark为nil或不属于单链表的情况下,返回nil。调用了insert,O(1)

  • val:待插节点值
  • mark:在此节点后插入,如果此节点属于单链表lst

PushBack

func (lst *SingleList)PushBack(val interface{}) *ListNode{
	end := lst.Back()
	if end == nil{
		return lst.InsertAfter(val,lst.head)
	}else {
		return lst.InsertAfter(val,end)
	}
}

生成值为val的节点,并插入链表尾节点后,调用Back和InsertAfter。返回指向生成节点的指针,O(n)。

  • val:待插节点值

PushFront

func (lst *SingleList)PushFront(val interface{}) *ListNode {
	return lst.InsertAfter(val,lst.head)
}

 生成值为val的节点,并插入链表头节点后,调用InsertAfter。返回指向生成节点的指针,O(1)。

  • val:待插节点值

remove

func (lst *SingleList)remove(e *ListNode) *ListNode{
	nextOne := e.next
	if nextOne == nil{
		return nil
	}
	lst.len--
	e.next = e.next.next
	nextOne.lst = nil
	return nextOne
}

 删除e的下一个节点,返回删除的节点,仅在此函数进行长度减少,其他删除函数调用此函数。e是尾节点时返回nil,O(1)。

  • e:单链表上的节点,需删除它后面的一个节点

RemoveAfter

func (lst *SingleList)RemoveAfter(e *ListNode) *ListNode{
	if e == nil{
		return nil
	}
	if e.lst == lst{
		return lst.remove(e)
	}
	return nil
}

删除指定节点的后一个节点,返回删除的节点,指定元素为尾节点或nil时,返回nil,O(1)

RemoveFront

func (lst *SingleList)RemoveFront() *ListNode {
	return lst.remove(lst.head)
}

前删,删除第一个节点,返回删除的节点,可能为nil,O(1)

测试

常见问题

怎么遍历呀?

像下面这样就可以了呦

for e := lst.Front(); e != nil; e = e.Next() {
   do something with e.Val
}

全部代码

具体代码和测试代码在:https://gitee.com/frankyu365/datastructure

参考

Go标准库-container/list

更多Go相关内容:Go-Golang学习总结笔记

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。如果您感觉有所收获,自愿打赏,可选择支付宝18833895206(小于),您的支持是我不断更新的动力。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是使用Go语言实现循环单链表的示例代码: ```go package main import ( "fmt" ) type Node struct { data interface{} next *Node } type CircularLinkedList struct { head *Node tail *Node size int } func (list *CircularLinkedList) getSize() int { return list.size } func (list *CircularLinkedList) isEmpty() bool { return list.head == nil } func (list *CircularLinkedList) addFirst(data interface{}) { newNode := &Node{data, nil} if list.isEmpty() { list.head = newNode list.tail = newNode newNode.next = newNode } else { newNode.next = list.head list.head = newNode list.tail.next = newNode } list.size++ } func (list *CircularLinkedList) addLast(data interface{}) { newNode := &Node{data, nil} if list.isEmpty() { list.head = newNode list.tail = newNode newNode.next = newNode } else { newNode.next = list.head list.tail.next = newNode list.tail = newNode } list.size++ } func (list *CircularLinkedList) removeFirst() interface{} { if list.isEmpty() { return nil } data := list.head.data list.head = list.head.next list.tail.next = list.head list.size-- if list.isEmpty() { list.tail = nil } return data } func (list *CircularLinkedList) display() { if list.isEmpty() { fmt.Println("List is empty") return } fmt.Println("List:") currentNode := list.head for { fmt.Printf("%v -> ", currentNode.data) currentNode = currentNode.next if currentNode == list.head { break } } fmt.Println() } func main() { list := &CircularLinkedList{} list.addFirst(1) list.addLast(2) list.addLast(3) list.addFirst(0) list.display() fmt.Println("Size:", list.getSize()) list.removeFirst() list.removeFirst() list.display() fmt.Println("Size:", list.getSize()) } ``` 输出结果: ``` List: 0 -> 1 -> 2 -> 3 -> Size: 4 List: 2 -> 3 -> Size: 2 ``` 该代码实现了循环单链表的基本功能,包括添加节点、删除节点、获取链表大小和显示链表内容等。在该示例中,循环单链表的头节点和尾节点都是存储在链表对象中的,而不是每个节点中都保存头节点和尾节点的指针。同时,该示例还实现了判断链表是否为空的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lady_killer9

感谢您的打赏,我会加倍努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值