Realization of golang double linked list

Keywords: Go Attribute

Realization of Double Link List

Basic concepts

Each node stores pointers to the previous and next nodes

Ideas for Realization

Create a node structure

  1. Each node has a pointer to the upper node and a pointer to the lower node.
  2. Each node has a key => value

Create a linked list structure

  1. Link List Capacity Size Attribute
  2. Link List Size Attribute
  3. Link list lock to achieve concurrent security
  4. Link Header Node
  5. End node of list

Implementation of Link List Operation Method

  1. Add Header Node Operation AppendHead
  2. Add tail node operation AppendTail
  3. Additional tail node operation Append
  4. Insert any node operation
  5. Delete any node operation Remove
  6. Delete header node operation RemoveHead
  7. Delete tail node operation RemoveTail
  8. Gets the node Get at the specified location
  9. Search for any node
  10. Get the list size GetSize
  11. Print all node operations
  12. Reverse all node operations

summary

  1. Learning algorithms well is a process of accumulation.
  2. In learning, we need to integrate knowledge with practice.
  3. 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

  1. Judge capacity first
  2. Determine whether the head is empty.

    1. If null, add a new node
    2. 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

  1. Judge capacity first
  2. Determine whether the tail is empty.

    1. If null, add a new node
    2. 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

  1. Judging capacity size
  2. Judging Link List Size
  3. First: If the insertion position is larger than the current length, the tail is added.
  4. Second: If the insertion position is equal to 0, add the header
  5. Third: Intermediate insertion nodes

    1. First remove the node to insert into the position of the node, pseudo-C node
    2. Between A and C, insert a B node
    3. The next node of A should be B, that is, the next node of C should be B.
    4. The upper node of B is the upper node of C.
    5. 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

  1. Judging whether the head is empty
  2. Remove the head node
  3. Determine whether the head has the next node

    1. If there is no next node, it means that there is only one node, deleting itself without moving the pointer position.
    2. 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

  1. Determine whether the tail node is empty
  2. Remove the tail node
  3. Determine whether the last node of the tail node exists

    1. Non-existent: It means that there is only one node, deleting itself, and needn't move the pointer position.
    2. 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

  1. Determine whether it is a header node
  2. Determine whether it is a tail node
  3. Otherwise, it is necessary to move the pointer position of the upper and lower nodes and delete the elements.

    1. Pointer the next node of the previous node to the next node
    2. 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
}

View the full source code

Posted by mrmigu on Sun, 11 Aug 2019 21:26:15 -0700