写在前面
结构定义
- Head结构
type Head[T any] struct {
// start 起始节点
start INode[T]
// length 链表长度
length uint64
// curr 当前节点
curr INode[T]
// end 结尾节点
end INode[T]
}
- 接口定义
// node接口
type INode[T any] interface {
Next() INode[T]
Value() T
SetNext(INode[T])
SetValue(T)
}
// head接口
type IHead[T any] interface {
First() INode[T]
Last() INode[T]
Length() uint64
IsEmpty() bool
Clear()
Curr() INode[T]
Next() INode[T]
// Append 尾插
Append(val T) IHead[T]
AppendNode(node INode[T]) IHead[T]
// Prepend 头插
Prepend(val T) IHead[T]
PrependNode(node INode[T]) IHead[T]
// Range 遍历
Range(fn func(node INode[T]) bool)
Slice() []T
Show() string
// Insert 插入
Insert(index uint64, val T) IHead[T]
InsertNode(index uint64, node INode[T]) IHead[T]
// Remove 移除
Remove(index uint64) IHead[T]
RemoveValue(val T) IHead[T]
RemoveHead() IHead[T]
RemoveTail() IHead[T]
// Reverse 反转
Reverse() IHead[T]
// Copy 复制
Copy() IHead[T]
// Find 查找
Find(fn func(val T) bool) (INode[T], bool)
FindIndex(fn func(val T) bool) (uint64, bool)
FindAll(fn func(val T) bool) []INode[T]
// ...
}
实现具体方法
type Option[T any] func(*Head[T])
func New[T any](opts ...Option[T]) IHead[T] {
head := &Head[T]{}
for _, opt := range opts {
opt(head)
}
return head
}
First
直接返回start节点, 该节点为单向链表起始节点
func (h *Head[T]) First() INode[T] {
return h.start
}
Length
返回链表元素个数, 理论上直接返回
length值即可, 但是INode具备SetNext方法, 因此可能会在尾节点追加一个链, 因此需要判断, 并维护length
func (h *Head[T]) Length() uint64 {
h.calcLength()
return h.length
}
// calcLength 判断end节点是否为实际的end节点, 如果不是, 重新维护end和length
func (h *Head[T]) calcLength() {
for h.end != nil && h.end.Next() != nil {
h.length++
h.end = h.end.Next()
}
}
IsEmpty
对length判断, 但是前面说了, 可能会被追加链到末尾, 因此也需要维护
func (h *Head[T]) IsEmpty() bool {
h.calcLength()
return h.length == 0
}
Last
同上, 维护
end和length, 并返回end
func (h *Head[T]) Last() INode[T] {
h.calcLength()
return h.end
}
Append
尾插法, 向链表尾部持续添加节点
func (h *Head[T]) Append(val T) IHead[T] {
h.length++
if h.start == nil {
h.setFirst(NewNode[T](val))
return h
}
h.end.SetNext(NewNode[T](val))
h.end = h.end.Next()
return h
}
Prepend
头插法, 向链表头部持续添加节点
func (h *Head[T]) Prepend(val T) IHead[T] {
h.length++
if h.start == nil {
h.setFirst(NewNode[T](val))
return h
}
node := NewNode[T](val)
node.SetNext(h.start)
h.start = node
return h
}
Insert
向指定索引位置插入数据, 这里插入的是数据, 不是节点, 所以基于该数据初始化节点, 然后调用节点插入方法插入
插入节点时候, 判断插入的位置是否属于特殊位置(头部或者尾部, 如果是, 则直接调用头插或者尾插结束, 如果不是, 则计算插入的位置, 断开原来的链, 与新链连接)
func (h *Head[T]) Insert(index uint64, val T) IHead[T] {
node := NewNode(val)
return h.InsertNode(index, node)
}
func (h *Head[T]) InsertNode(index uint64, node INode[T]) IHead[T] {
if index == 0 {
return h.PrependNode(node)
}
if index >= h.length {
return h.AppendNode(node)
}
prev := h.start
for i := uint64(1); i < index; i++ {
prev = prev.Next()
}
// 计算插入的节点是不是一个链表, 如果是, 要把链表维护进当前链表, 也就是头节点插入index位置, 尾节点与当前链表断开处相连
n := node
h.length++
for n.Next() != nil {
h.length++
n = n.Next()
}
n.SetNext(prev.Next())
prev.SetNext(node)
return h
}
Remove
删除指定位置的节点, 判断index是否属于特殊位置, 如果是, 则直接调用删除头节点或者尾节点方法即可.
RemoveValue接收一个回调函数, 因为该链表是范型, 内部不好直接判断(比较), 因此暴露比较的回调函数参数, 由用户自行实现比较函数(例如对象是个自定义结构体或者map, 用户可以根据其中某一个属性判断), 然后把匹配的节点都删除, 这样就完成了根据节点值删除节点的功能
func (h *Head[T]) Remove(index uint64) IHead[T] {
if index >= h.Length() {
return h
}
if index == h.Length()-1 {
return h.RemoveTail()
}
if index == 0 {
return h.RemoveHead()
}
prev := h.start
for i := uint64(1); i < index; i++ {
prev = prev.Next()
}
prev.SetNext(prev.Next().Next())
h.length--
return h
}
func (h *Head[T]) RemoveValue(fn func(val T) bool) IHead[T] {
if h.IsEmpty() {
return h
}
for fn(h.start.Value()) {
h.start = h.start.Next()
h.length--
}
prev := h.start
curr := h.start.Next()
for curr != nil {
if fn(curr.Value()) {
prev.SetNext(curr.Next())
h.length--
curr = prev.Next()
continue
}
prev = curr
curr = curr.Next()
}
h.end = prev
return h
}
func (h *Head[T]) RemoveTail() IHead[T] {
if h.IsEmpty() {
return h
}
if h.length == 1 {
h.start = nil
h.curr = nil
h.end = nil
h.length = 0
return h
}
prev := h.start
for i := uint64(1); i < h.Length()-1; i++ {
prev = prev.Next()
}
prev.SetNext(nil)
h.length--
return h
}
func (h *Head[T]) RemoveHead() IHead[T] {
if h.IsEmpty() {
return h
}
h.start = h.start.Next()
h.length--
return h
}
基本使用
package main
import (
"fmt"
"github.com/aide-cloud/slices/linked/singly"
)
func main() {
h := singly.New(singly.WithValues(1, 2, 3, 4, 5))
fmt.Println(h.Show())
list := h.Slice()
fmt.Println(fmt.Sprintf("cap: %d, len: %d, %v", cap(list), len(list), list))
h.Append(6).Append(7).Append(8).Append(9).Append(10)
fmt.Println(h.Show())
list = h.Slice()
fmt.Println(fmt.Sprintf("cap: %d, len: %d, %v", cap(list), len(list), list))
h.Remove(1)
fmt.Println(h.Show())
h.RemoveValue(func(val int) bool {
return val >= 8
})
h.Prepend(100).Append(200).Append(300).Append(400).Append(500)
fmt.Println(h.Show())
list = h.Slice()
fmt.Println(fmt.Sprintf("cap: %d, len: %d, %v", cap(list), len(list), list))
}
输出
(1)-->(2)-->(3)-->(4)-->(5)
cap: 5, len: 5, [1 2 3 4 5]
(1)-->(2)-->(3)-->(4)-->(5)-->(6)-->(7)-->(8)-->(9)-->(10)
cap: 10, len: 10, [1 2 3 4 5 6 7 8 9 10]
(1)-->(3)-->(4)-->(5)-->(6)-->(7)-->(8)-->(9)-->(10)
(100)-->(1)-->(3)-->(4)-->(5)-->(6)-->(7)-->(200)-->(300)-->(400)-->(500)
cap: 11, len: 11, [100 1 3 4 5 6 7 200 300 400 500]
最后
内部还实现了一些其他的方法, 例如头插节点, 尾插节点等等, 感兴趣的同学可以通过仓库查看