Definition of a doubly linked list
A doubly linked list is also called a doubly linked list, which is a list of two pointers in each data node, pointing directly to successive and direct precursors respectively. So, starting from any node in a doubly linked list, it is easy to access its predecessor and successor nodes. In general, we construct two-way circular linked lists.
Here's a record of your learning comprehension process.
Graphic
[Image upload failed ...] (image-afe880-1531019243291)]
Go's source code implementation
1. First look at the definition of the elements stored in the list (element):
// 双向链表的一个元素 type Element struct { // 前驱指针和后继指针 prev, next *Element // 该元素属于哪个链表list list *List // 该元素存储的值 Value interface{} }
2. Define two methods for the element structure:
// Next 返回元素e的后一个元素 func (e *Element) Next() *Element { if p := e.next; e.list != nil && &e.list.root != p { return p } return nil }// Prev 返回元素e的前一个元素 func (e *Element) Prev() *Element { if p := e.prev; e.list != nil && &e.list.root != p { return p } return nil }
3. Look again at the definition of the list of lists:
// List 代表一个双向链表 // List的零值是一个空的列表 type List struct { // 根节点 root Element // 当前链表的长度 len int }
4. Define an initialization method for a list of lists
// Init 初始化一个链表,或者重置一个链表 func (l *List) Init() (*List) { l.root.prev = &l.root l.root.next = &l.root l.len = 0 return l }
5. Define a factory method for the list of lists that is used to generate a linked list:
func New() *List { return new(List).Init() }
6. The following are the two ways to see the core of the list: Insert and delete, the other way of the list is basically based on these two methods
// insert 在元素at后面插入元素e,将list的长度递增,返回该元素 func (l *List) insert(e, at *Element) *Element { n := at.next at.next = e e.prev = at e.next = n n.prev = e e.list = l l.len ++ return e }// remove 从双向链表中移除一个元素e,递减链表的长度,返回该元素e func (l *List) remove(e *Element) *Element { e.prev.next = e.next e.next.prev = e.prev e.next = nil // 防止内存泄漏 e.prev = nil // 防止内存泄漏 e.list = nil l.len -- return e }
Doubly linked list insert operate
Insert operation:
- Remove the successor pointer to element B and the precursor pointer of element C first
- The successor pointer to element B is then pointed to the new element, and the precursor pointer to new element A is pointed to element b
- Again, the precursor pointer to element C points to the new element, and the successor pointer to the new element, C
- Finally, add the link list length to a
Double linked list Remove operate
Delete operation:
- The successor pointer to element B is now pointed to element D, and the precursor pointer of element d points to B
- Then the precursor pointer and the successor pointer of element c are deleted.
- Reduce the link table length by one
7. Understanding the insertion and deletion of the linked list, you can then encapsulate a rich list of operation functions:
// insertValue 是对l.insert(&Element{Value:v}, at)的包装func (l *List) insertValue(v interface{}, at *Element) *Element { return l.insert(&Element{Value: v}, at)}// Remove 如果元素e是链表l的一个元素,则移除e// 返回元素e的值e.Value// 该元素e不能为nilfunc (l *List) Remove(e *Element) interface{} { if e.list == l { l.remove(e) } return e.Value}
To insert an element in the head or tail of a linked list:
// PushFront 插入一个包含值v的新元素e到链表l的头部,并返回该元素efunc (l *List) PushFront(v interface{}) *Element { l.lazyInit() return l.insertValue(v, &l.root)}// PushBack 插入一个包含值v的新元素e到链表l的尾部,并返回这个新元素efunc (l *List) PushBack(v interface{}) *Element { l.lazyInit() return l.insertValue(v, l.root.prev)}
Insert a new element before or after an element:
// InsertBefore 在元素mark之前插入一个值为v的新元素// 如果mark不属于链表l,则不会更新链表l,mark也不能为nilfunc (l *List) InsertBefore(v interface{}, mark *Element) *Element { if mark.list != nil { return nil } return l.insertValue(v, mark.prev)}// InsertAfter 在元素mark之后插入一个值为v的新元素// 如果mark不属于链表l,则不会更新链表l,mark也不能为nilfunc (l *List) InsertAfter(v interface{}, mark *Element) *Element { if mark.list != nil { return nil } return l.insertValue(v, mark)}
To move an element to the head or tail of a linked list:
// MoveToFront 将元素e移动到链表头部// 如果元素e不是链表的元素,则不会更新链表func (l *List) MoveToFront(e *Element) { if e.list != l || l.root.next == e { return } l.insert(l.remove(e), &l.root)}// MoveToBack 将元素e移动到链表尾部// 如果元素e不是链表的元素,则不会更新链表func (l *List) MoveToBack(e *Element) { if e.list != l || e == l.root.prev { return } l.insert(l.remove(e), l.root.prev)}
Move element A before or after element B:
// MoveBefore 移动元素e到元素mark之前func (l *List) MoveBefore(e, mark *Element) { if e.list != l || mark.list != l || e == mark { return } l.insert(l.remove(e), mark.prev)}// MoveAfter 移动元素e到元素mark之后func (l *List) MoveAfter(e, mark *Element) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(e), mark)}
The above code is from Golang source, see Go Doc
Summarize
Doubly linked list is not difficult to understand, as long as the understanding of its data structure and insertion, deletion principle, can quickly grasp.
Reference:
- Doubly linked List-wikipedia
- Container/list-go Doc
- Introduction of arrays, single-linked lists and doubly-linked lists and C/c++/java implementations of doubly linked lists
Original address: https://popwalker.github.io/article/c78375f9/