链表是一种线性表,单并非是顺序存储的;而是链表上的每一个节点都有指向其它节点的指针.链表有单向链表和双向链表之分; 单向链表的每个节点有数据域和指向下一节点的指针组成;
单向链表分为带头节点和不带头节点;不带头节点的链表在做插入/删除操作的时候需要做一些非空判断;
头节点和头指针
- 头节点:放了方便操作链表,放在第一个有效节点之前的节点;其next指针指向第一个有效节点
- 头指针:链表的第一个节点指针;若有头节点则指向头节点,否则指向第一个有效节点
- 链表可以没头节点,但不能无头指针;
定义节点和链表
// Node 链表节点
type Node struct {
value int
next *Node
}
// List 单向链表
type List struct {
len int
head *Node
}
1.链表的初始化及清空
// New 新建链表
func New() *List {
return new(List).Reset()
}
// Reset 清空链表
func (l *List) Reset() *List {
l.len = 0
if l.head == nil {
l.head = &Node{0, nil}
}
l.head.next = nil
return l
}
2.链表的基本情况操作
// Len 链表长度
func (l *List) Len() int {
return l.len
}
// IsEmpty 是否是空表
func (l *List) IsEmpty() bool {
return l.First() == nil
}
// First 链表的第一个有效节点
func (l *List) First() *Node {
return l.head.next
}
// Last 链表的最后一个节点,空表则返回nil
func (l *List) Last() *Node {
if la := l.last(); la != l.head {
return la
}
return nil
}
// last 若为空表,则返回头节点,否则返回第一个有效节点(非nil)
func (l *List) last() *Node {
h := l.head
for h != nil && h.next != nil {
h = h.next
}
return h
}
3.查找某个节点的前一个节点
// Before 节点at前一个节点
func (l *List) Before(at *Node) *Node {
b := l.before(at)
if b == nil {
// 不包含节点
return nil
}
if b == l.head {
return nil
}
return b
}
// before 节点at前一个节点,若at为第一个有效节点,则返回头节点
func (l *List) before(at *Node) *Node {
if at == nil {
return nil
}
h := l.head
for h != nil && h.next != at {
h = h.next
}
return h
}
4.插入新元素,分为4中情况
- 4.1头插入插入元素
// InsertFront 头插法插入新元素
func (l *List) InsertFront(v int) *Node {
nn := l.insertValue(v, l.head)
l.len++
return nn
}
- 4.2尾插法插入新元素
// InsertLast 尾插法插入新元素
func (l *List) InsertLast(v int) *Node {
la := l.last()
nn := l.insertValue(v, la)
l.len++
return nn
}
- 4.3在某一节点之前插入新元素
func (l *List) InsertBefore(v int, at *Node) *Node {
b := l.before(at)
if b == nil {
// 不包含节点
return nil
}
nn := l.insertValue(v, b)
l.len++
return nn
}
- 4.4在某一节点之后插入新元素
func (l *List) InsertAfter(v int, at *Node) *Node {
nn := l.insertValue(v, at)
l.len++
return nn
}
- 这是两个插入元素的辅助方法
// insert 在at节点后面插入节点n
func (l *List) insert(n, at *Node) *Node {
nn := at.next
n.next = nn
at.next = n
return n
}
// insertValue 在at节点后面插入新元素v
func (l *List) insertValue(v int, at *Node) *Node {
nn := &Node{v, nil}
l.insert(nn, at)
return nn
}
5.删除某一节点
func (l *List) Delete(n *Node) int {
f := l.before(n)
if f == nil {
// 不包含节点
return n.value
}
f.next = n.next
n.next = nil
l.len--
return n.value
}
6.移动节点,分为两种情况
- 6.1 移动节点A到节点B之后
func (l *List) MoveAfter(source, dest *Node) *Node {
if source == nil || dest == nil {
return source
}
bs := l.before(source)
ds := l.before(dest)
if bs == nil || ds == nil {
// 不包含节点
return source
}
bs.next = source.next
source.next = dest.next
dest.next = source
return source
}
- 6.2 移动节点A到节点B之前
func (l *List) MoveBefore(source, dest *Node) *Node {
if source == nil || dest == nil {
return source
}
bs := l.before(source)
ds := l.before(dest)
if bs == nil || ds == nil {
// 不包含节点
return source
}
bs.next = source.next
source.next = dest
ds.next = source
return source
}
7.反转链表
func (l *List) Reversed() *List {
if l.IsEmpty() {
return l
}
pre := (*Node)(nil)
n := l.First()
for n != nil {
nn := n.next
n.next = pre
if nn == nil {
l.head.next = n
break
}
pre = n
n = nn
}
return l
}
这里详细说明下反转链表的步骤
链表原型: head-->A-->B-->C-->D-->nil
- 1:链表为空,直接返回
- 2:链表非空,比如操作C, 取出当前节点C
- 3:取出下一节点D; d==c.next
- 4:判断D是否为nil,为nil设置头节点的next = c ,结束循环
- 5:D不为nil,C.next = pre(前一节点)
- 6:重新赋值pre为当前节点: pre= C
- 7:把当前节点n设为D: n=d
注意代码中的pre定义
pre := (*Node)(nil)
这里,因为有头节点的存在,我们可以在反转的时候把头节点想象成nil
8.最后来一个方便查看链表分布的方法
// 空表: "nil"
// 非空: "1-->2-->3-->nil"
func (l *List) Str() string {
if l.len == 0 {
return "nil"
}
var b strings.Builder
for r := l.head.next; r != nil; {
vs := strconv.FormatInt(int64(r.value), 10)
b.WriteString(vs)
b.WriteString("-->")
r = r.next
}
b.WriteString("nil")
return b.String()
}