题目
除了泛型函数之外,Go 还支持泛型类型。 类型可以使用类型参数进行参数化,这对于实现通用数据结构非常有用。
此示例展示了能够保存任意类型值的单链表的简单类型声明。
作为练习,请为此链表的实现添加一些功能。
package main
// List represents a singly-linked list that holds
// values of any type.
type List[T any] struct {
next *List[T]
val T
}
func main() {
}
解答
为了增强泛型链表的功能,我们为List[T]
类型添加了多个方法,包括追加元素、插入头部、获取长度、转换为字符串、获取元素、插入元素、删除元素和反转链表。以下是完整的实现代码:
package main
import (
"fmt"
"strings"
)
// List 表示一个可以保存任何类型的值的单链表。
type List[T any] struct {
next *List[T]
val T
}
// Append 将值追加到链表末尾,返回新的头节点。
func (l *List[T]) Append(val T) *List[T] {
if l == nil {
return &List[T]{val: val, next: nil}
}
current := l
for current.next != nil {
current = current.next
}
current.next = &List[T]{val: val, next: nil}
return l
}
// PushFront 将值插入到链表头部,返回新的头节点。
func (l *List[T]) PushFront(val T) *List[T] {
return &List[T]{val: val, next: l}
}
// Length 返回链表的长度。
func (l *List[T]) Length() int {
count := 0
current := l
for current != nil {
count++
current = current.next
}
return count
}
// String 返回链表的字符串表示。
func (l *List[T]) String() string {
var sb strings.Builder
sb.WriteString("[")
current := l
for current != nil {
sb.WriteString(fmt.Sprint(current.val))
current = current.next
if current != nil {
sb.WriteString(", ")
}
}
sb.WriteString("]")
return sb.String()
}
// Get 获取指定位置的元素,如果位置有效返回元素和true,否则返回零值和false。
func (l *List[T]) Get(pos int) (T, bool) {
var zero T
current := l
for i := 0; i < pos; i++ {
if current == nil {
return zero, false
}
current = current.next
}
if current == nil {
return zero, false
}
return current.val, true
}
// Insert 在指定位置插入元素,返回新的头节点。
// 如果位置大于链表长度,不插入。
func (l *List[T]) Insert(pos int, val T) *List[T] {
if pos == 0 {
return &List[T]{val: val, next: l}
}
prev := l
for i := 0; i < pos-1; i++ {
if prev == nil {
return l // 位置无效,不插入
}
prev = prev.next
}
if prev == nil {
return l
}
newNode := &List[T]{val: val, next: prev.next}
prev.next = newNode
return l
}
// Remove 删除指定位置的元素,返回新的头节点。
// 如果位置无效,返回原链表。
func (l *List[T]) Remove(pos int) *List[T] {
if l == nil {
return nil
}
if pos == 0 {
return l.next
}
prev := l
for i := 0; i < pos-1; i++ {
if prev.next == nil {
return l // 无效位置
}
prev = prev.next
}
if prev.next != nil {
prev.next = prev.next.next
}
return l
}
// Reverse 反转链表,返回新的头节点。
func (l *List[T]) Reverse() *List[T] {
var prev *List[T]
current := l
for current != nil {
next := current.next
current.next = prev
prev = current
current = next
}
return prev
}
func main() {
var list *List[int]
list = list.Append(1)
list = list.Append(2)
list = list.Append(3)
fmt.Println(list.String()) // [1, 2, 3]
list = list.PushFront(0)
fmt.Println(list.String()) // [0, 1, 2, 3]
fmt.Println("Length:", list.Length()) // 4
val, ok := list.Get(2)
fmt.Printf("Get(2): %v, %v\n", val, ok) // 2, true
list = list.Insert(2, 99)
fmt.Println(list.String()) // [0, 1, 99, 2, 3]
list = list.Remove(1)
fmt.Println(list.String()) // [0, 99, 2, 3]
list = list.Reverse()
fmt.Println(list.String()) // [3, 2, 99, 0]
}
说明
- Append:将元素追加到链表末尾。处理空链表情况。
- PushFront:在链表头部插入元素,返回新头节点。
- Length:遍历链表计算长度。
- String:生成链表的字符串表示,便于调试。
- Get:根据索引获取元素,处理越界情况。
- Insert:在指定位置插入元素,处理头部插入和越界。
- Remove:删除指定位置元素,处理头部删除和越界。
- Reverse:反转链表,返回新头节点。
这些方法增强了链表的功能,使其支持基本操作,并处理了空链表和边界条件。