用go实现向链表----数据结构与算法之旅1

1,346 阅读4分钟

链表是一种线性表,单并非是顺序存储的;而是链表上的每一个节点都有指向其它节点的指针.链表有单向链表和双向链表之分; 单向链表的每个节点有数据域和指向下一节点的指针组成;

单向链表分为带头节点和不带头节点;不带头节点的链表在做插入/删除操作的时候需要做一些非空判断;

头节点和头指针

  • 头节点:放了方便操作链表,放在第一个有效节点之前的节点;其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()
}

源码地址,包含测试,测试覆盖率为100%

github.com/zgjff/Blog/…