Realization of Double Link List
Basic concepts
Each node stores pointers to the previous and next nodes
Ideas for Realization
Create a node structure
- Each node has a pointer to the upper node and a pointer to the lower node.
- Each node has a key => value
Create a linked list structure
- Link List Capacity Size Attribute
- Link List Size Attribute
- Link list lock to achieve concurrent security
- Link Header Node
- End node of list
Implementation of Link List Operation Method
- Add Header Node Operation AppendHead
- Add tail node operation AppendTail
- Additional tail node operation Append
- Insert any node operation
- Delete any node operation Remove
- Delete header node operation RemoveHead
- Delete tail node operation RemoveTail
- Gets the node Get at the specified location
- Search for any node
- Get the list size GetSize
- Print all node operations
- Reverse all node operations
summary
- Learning algorithms well is a process of accumulation.
- In learning, we need to integrate knowledge with practice.
- More use case testing is required for implementation.
Code parsing
Structures that define nodes
type DoubleNode struct { Key int //key Value interface{} //value Prev *DoubleNode //Last node pointer Next *DoubleNode //Next node pointer Freq int //Frequency. For LFU }
Define the structure of a double linked list
//Define the structure of a double linked list type DoubleList struct { lock *sync.RWMutex //lock Capacity uint //Maximum capacity Size uint //Current capacity Head *DoubleNode //Header node Tail *DoubleNode //Tail node }
Initial double linked list
//Initial double linked list func New(capacity uint) *DoubleList { list := new(DoubleList) list.Capacity = capacity list.lock = new(sync.RWMutex) list.Size = 0 list.Head = nil list.Tail = nil return list }
Add Header Node
Ideas for Realization
- Judge capacity first
-
Determine whether the head is empty.
- If null, add a new node
- If not null, change the existing node and add the
func (list *DoubleList) AddHead(node *DoubleNode) bool { //Determine whether the capacity is 0 or not if list.Capacity == 0 { return false } list.lock.Lock() defer list.lock.Unlock() //Judging whether the head node is nil if list.Head == nil { list.Head = node list.Tail = node } else { //There are header nodes list.Head.Prev = node //Point a node on an old header node to a new node node.Next = list.Head //The next node of the new header node points to the old header node list.Head = node //Setting up a new header node list.Head.Prev = nil //Setting NIL for a node on a new header node } list.Size++ return true }
Adding tail elements
Ideas for Realization
- Judge capacity first
-
Determine whether the tail is empty.
- If null, add a new node
- If not null, change the existing node and add the
func (list *DoubleList) AddTail(node *DoubleNode) bool { //Judging whether there is capacity or not, if list.Capacity == 0 { return false } list.lock.Lock() defer list.lock.Unlock() //Judging whether the tail is empty if list.Tail == nil { list.Tail = node list.Head = node } else { //The old tail next node points to the new node list.Tail.Next = node //When adding a new node, first point the node's upper node to the old tail node node.Prev = list.Tail //Setting up a new tail node list.Tail = node //New tail next node set to empty list.Tail.Next = nil } //Double linked list size + 1 list.Size++ return true }
Add any location element
Ideas for Realization
- Judging capacity size
- Judging Link List Size
- First: If the insertion position is larger than the current length, the tail is added.
- Second: If the insertion position is equal to 0, add the header
-
Third: Intermediate insertion nodes
- First remove the node to insert into the position of the node, pseudo-C node
- Between A and C, insert a B node
- The next node of A should be B, that is, the next node of C should be B.
- The upper node of B is the upper node of C.
- The next node of B is C.
//Add any location element func (list *DoubleList) Insert(index uint, node *DoubleNode) bool { //Full capacity if list.Size == list.Capacity { return false } //If there are no nodes if list.Size == 0 { return list.Append(node) } //If the insertion position is larger than the current length, the tail is added if index > list.Size { return list.AddTail(node) } //If the insertion position equals zero, the header adds if index == 0 { return list.AddHead(node) } //Remove the node to be inserted nextNode := list.Get(index) list.lock.Lock() defer list.lock.Unlock() //Intermediate insertion: //Assuming that there are already A and C nodes, now insert B nodes. // nextNode is the C node. //The next node of A should be B, that is, the next node of C should be B. nextNode.Prev.Next = node //The upper node of B is the upper node of C. node.Prev = nextNode.Prev //The next node of B is C. node.Next = nextNode //The upper node of C is B. nextNode.Prev = node list.Size++ return true }
Delete header nodes
Ideas for Realization
- Judging whether the head is empty
- Remove the head node
-
Determine whether the head has the next node
- If there is no next node, it means that there is only one node, deleting itself without moving the pointer position.
- If there is a next node, the next node in the head is the head node.
//Delete header nodes func (list *DoubleList) RemoveHead() *DoubleNode { //Determine whether the head node is empty if list.Head == nil { return nil } list.lock.Lock() defer list.lock.Unlock() //Remove the head node node := list.Head //Determine whether the head has the next node if node.Next != nil { list.Head = node.Next list.Head.Prev = nil } else { //If there is no next node, there is only one node. list.Head, list.Tail = nil, nil } list.Size-- return node }
Delete tail nodes
Ideas for Realization
- Determine whether the tail node is empty
- Remove the tail node
-
Determine whether the last node of the tail node exists
- Non-existent: It means that there is only one node, deleting itself, and needn't move the pointer position.
- If there is a previous node, the last node at the tail is the tail node.
//Delete tail nodes func (list *DoubleList) RemoveTail() *DoubleNode { //Determine whether the tail node is empty if list.Tail == nil { return nil } list.lock.Lock() defer list.lock.Unlock() //Remove the tail node node := list.Tail //Determine whether the last one of the tail nodes exists if node.Prev != nil { list.Tail = node.Prev list.Tail.Next = nil } list.Size-- return node }
Delete any element
Ideas for Realization
- Determine whether it is a header node
- Determine whether it is a tail node
-
Otherwise, it is necessary to move the pointer position of the upper and lower nodes and delete the elements.
- Pointer the next node of the previous node to the next node
- Point the pointer of the previous node of the next node to the upper node
//Delete any element func (list *DoubleList) Remove(node *DoubleNode) *DoubleNode { //Determine whether it is a header node if node == list.Head { return list.RemoveHead() } //Determine whether it is a tail node if node == list.Tail { return list.RemoveTail() } list.lock.Lock() defer list.lock.Unlock() //Nodes are intermediate nodes //Need: //Pointer the next node of the previous node to the next node //Point the pointer of the previous node of the next node to the upper node node.Prev.Next = node.Next node.Next.Prev = node.Prev list.Size-- return node }