Linked list theory based on Algorithm

1. Basic definition of linked list

We have summarized the array of one of the two data structures before. This time, we will summarize the other - Linked List. Firstly, the bottom layers of dynamic array, stack and queue all rely on static array; Using resize to solve the fixed capacity problem may cause a lot of waste of memory space (for example, when the array is full, it will automatically double the capacity, but if you don't continue to add elements, it will actually waste twice the capacity); A large number of elements need to be moved during insertion and deletion. The Linked List is a real dynamic data structure.
One sentence summary: data is stored in 'nodes'.

If the next of a node is empty, it is the last node. Therefore, the advantage of linked list is to realize real dynamic without dealing with the problem of fixed capacity; The disadvantage is that it loses the ability of random access (because the location of linked list nodes in the underlying memory of the computer is not continuous).
Therefore, compare it with the array as follows:

2. Addition, deletion, modification and query of linked list

2.1 basic functions

The first element of the linked list is defined as head.

class LinkedList:
    class _Node:
        def __init__(self, e=None):
            self.e = e
            self.next = None

        def __str__(self):
            return str(self.e)

        def __repr__(self):
            return self.__str__()

    def __init__(self):
        self.__head = None
        self._size = 0

    def get_size(self):
        return self._size

    def is_empty(self):
        return self._size == 0

2.2 adding elements to linked list

Previously, it was convenient for us to add elements at the end of the array (because the size variable of the array points to the next location of the last element in the array, that is, the location of the next added element), while it is more convenient for the linked list to add elements at the head.
For example, now we want to add a node with the content of 666 to the head of the linked list without destroying the original structure of the linked list.

First point the next of node 666 to the current linked list head.

Then point the head to the 666 node.

The implementation of adding header is as follows (similar to stack):

def add_first(self, e):
        node = self._Node(e)
        node.next = self.__head
        self.__head = node
        self._size += 1

The implementation of tail addition is similar to queue:

def add_last(self, e):
        node = self._Node(e)
        if self.__head is None:
            self.__head = node
            self._size += 1
            return
        cur = self.__head
        pre = None
        while cur:
            pre = cur
            cur = cur.next
            # When cur is the last node, pre is updated to the last node, and cur is updated to the last node. The next node is empty,
            # The next while cur will exit the loop. At this time, pre represents the last node. Just hang the node behind pre
        pre.next = node
        self._size += 1
        # Find out whether there is an item in the linked list. If there is, return True; if not, return False

Now we want to add elements anywhere in the linked list, for example, add element 666 where the 'index' (the linked list actually has no concept of index) is 2, that is, we need to insert a 666 between 1 and 2.

First, we need to search who the node before 666 is. Obviously, after we insert 666, the index is 2, then the previous node will traverse from 0 to the position with index 1 (prev finds 1).

Then insert 666 (pay attention to the order!).


Therefore, the key is to find the previous position of the node to be inserted (the position pointed by prev). However, if we want to insert the position of the head, the head has no front position. At this time, we need to do a special treatment. The specific implementation is as follows:

def add(self, index, e):
        if index < 0 or index > self._size:
            raise ValueError('Add failed. Illegal index.')
        if index == 0:
            #Special case: insert it into the head of the linked list,
            self.add_first(e)
            return
        prev = self.__head
        for i in range(index-1):
            prev = prev.next
        node = self._Node(e)
        node.next = prev.next
        prev.next = node
        self._size += 1 

Then add in front_ First and add_last can call add:

def add_first(self, e):
        self.add(0, e)
def add_last(self, e):
        self.add(self._size, e)

In addition, when we think about inserting 666, can we change the order of the two steps? First of all, if you point prev to 666 first, then 2 does not point to it, then naturally you can't find its position.


Then we build a function to print the linked list to check whether the inserted program is correct.

def __str__(self):
        curr = self.__head
        data = []
        while curr:
            data.append(str(curr.e))
            curr = curr.next
        return '(Head) ' + \
            ' -> '.join(data) + ' (Tail)'

def __repr__(self):
        return self.__str__()

Check it out!

if __name__ == '__main__':
    linkedlist = LinkedList()
    print(linkedlist.get_size())
    linkedlist.add_first(1)
    linkedlist.add_first(2)
    linkedlist.add_first(4)
    linkedlist.add_first(5)
    print(linkedlist)
    linkedlist.add(1,6)
    print(linkedlist)
0
(Head) 5 -> 4 -> 2 -> 1 (Tail)
(Head) 5 -> 6 -> 4 -> 2 -> 1 (Tail)

Let's delve into why adding elements to the head of the linked list is more special? Because the head does not have a previous node, it is logically special. However, we can set up a virtual head node (without storing any elements) in front of the head, that is, the real head node of the linked list (dummy head), but this virtual head node is shielded from users. At this time, there is no need for special processing of the head node.


With this virtual head node, we can rewrite the previous functions:

class LinkedList:
    class _Node:
        def __init__(self, e=None):
            self.e = e
            self.next = None

        def __str__(self):
            return str(self.e)

        def __repr__(self):
            return self.__str__()
    '''
    Original initialization
    def __init__(self):
        self.__head = None
        self._size = 0
    '''
    def __init__(self):
        self._dummy_head = self._Node()#The node of dummyhead has no content
        self._size = 0

    def get_size(self):
        return self._size

    def is_empty(self):
        return self._size == 0

    def add_first(self, e):
        self.add(0, e)

    def add_last(self, e):
        self.add(self._size, e)

    def add(self, index, e):
        if index < 0 or index > self._size:
            raise ValueError('Add failed. Illegal index.')
        '''
        if index == 0:
            self.add_first(e)
            return
        prev = self.__head
        '''
        prev = self._dummy_head
        for i in range(index-1):
            prev = prev.next
        node = self._Node(e)
        node.next = prev.next
        prev.next = node
        self._size += 1

   
    def __str__(self):
        '''
        curr = self.__head
        '''
        curr = self._dummy_head.next
        data = []
        while curr:
            data.append(str(curr.e))
            curr = curr.next
        return '<chapter_04_LinkedList.linkedlist.LinkedList>: (Head) ' + \
            ' -> '.join(data) + ' (Tail)'

    def __repr__(self):
        return self.__str__()

2.3 linked list traversal, query and update

def get(self, index):
    if index < 0 or index >= self._size:
        raise ValueError('Get failed. Illegal index.')
    curr = self._dummy_head.next
    for i in range(index):
        curr = curr.next
    return curr.e
  

def get_first(self):
    return self.get(0)

def get_last(self):
    return self.get(self._size - 1)

def setter(self, index, e):
    if index < 0 or index >= self._size:
        raise ValueError('Set failed. Illegal index.')
    curr = self._dummy_head.next
    for i in range(index):
        curr = curr.next
    curr.e = e

def contains(self, e):
    curr = self._dummy_head.next
    while curr:
        if curr.e == e:
            return True
        curr = curr.next
    return False

def __str__(self):
    curr = self._dummy_head.next
    data = []
    while curr:
        data.append(str(curr.e))
        curr = curr.next
    return '<chapter_04_LinkedList.linkedlist.LinkedList>: (Head) ' + \
        ' -> '.join(data) + ' (Tail)'

def __repr__(self):
    return self.__str__()

Inspection:

if __name__ == '__main__':
    linkedlist = LinkedList()
    print(linkedlist.get_size())
    linkedlist.add_first(1)
    linkedlist.add_first(2)
    linkedlist.add_first(4)
    linkedlist.add_first(5)

    print(linkedlist.get_size())
    print(linkedlist)
    print(linkedlist.get(3))
    print(linkedlist.get_first())
    print(linkedlist.get_last())
    linkedlist.setter(3,6)
    print(linkedlist)
0
4
<chapter_04_LinkedList.linkedlist.LinkedList>: (Head) 5 -> 4 -> 2 -> 1 (Tail)
1
5
1
<chapter_04_LinkedList.linkedlist.LinkedList>: (Head) 5 -> 4 -> 2 -> 6 (Tail

2.4 deleting elements from linked list

For example, we need to delete the element with index 2 (remember that the linked list has no index, just for the convenience of explanation here).

First, we need to find the node before 2, that is, the point of prev.


Skip 2. Its position cannot be found in the current 2 order.

In addition, in order to recycle space as much as possible in python, we manually make the node of 2 empty, that is, delNode.next = null. At this time, 2 is deleted from the linked list in the real sense.

def remove(self, index):
    if index < 0 or index >= self._size:
        raise ValueError('Remove failed. Illegal index.')
    prev = self._dummy_head
    for i in range(index):
        prev = prev.next
    ret = prev.next
    prev.next = ret.next
    ret.next = None
    self._size -= 1
    return ret.e

def remove_first(self):
    return self.remove(0)

def remove_last(self):
    return self.remove(self._size - 1)

def __str__(self):
    curr = self._dummy_head.next
    data = []
    while curr:
        data.append(str(curr.e))
        curr = curr.next
    return '<chapter_04_LinkedList.linkedlist.LinkedList>: (Head) ' + \
        ' -> '.join(data) + ' (Tail)'

def __repr__(self):
    return self.__str__()

Inspection:

if __name__ == '__main__':
    linkedlist = LinkedList()
    print(linkedlist.get_size())
    linkedlist.add_first(1)
    linkedlist.add_first(2)
    linkedlist.add_first(4)
    linkedlist.add_first(5)

    print(linkedlist)
    linkedlist.remove(2)
    linkedlist.remove_first()
    linkedlist.remove_last()
    print(linkedlist)
0
<chapter_04_LinkedList.linkedlist.LinkedList>: (Head) 5 -> 4 -> 2 -> 1 (Tail)
<chapter_04_LinkedList.linkedlist.LinkedList>: (Head) 4 (Tail)

3. Static linked list

This content is to transform one data structure into another, but it is difficult and not common in the interview. It belongs to the content of expanding ideas.
The linked list and the new linked list forms involved below all rely on pointers, but the early programming languages did not have pointers. Some people came up with the idea of using arrays instead of pointers to represent single linked lists.
First, let's make the elements of the array consist of two data fields (val represents the data content and cur is equivalent to the next pointer in the linked list). In addition, we do special processing for the first and last elements of the array, and do not store data (both intentionally waste these two spaces).

Let's take an example. Now that we have stored a set of data (a, B, C... G) in the static linked list, we can see that a has the cursor value of the next element B (i.e. 2), and so on. Until G is the last element with value, we set the cur of G to 0. The last element stores the subscript (i.e. 1) of the first value element, and the first element stores the subscript (i.e. 7) of the first position of the alternate list

Now we're going to insert C between B and D. in the first step, we throw C first in the free space (i.e. 7), in the second step, tell B to change its cur to 7, and in the third step, change C's cur to 3. It's done! In fact, the principle of single linked list insertion is similar.

#Static linked list
class Node:
    def __init__(self, cur, val=None):
       # value
       self.val = val
       # Cursor. The cursor of the last element must be 0
       self.cur = cur
    
class linkList:
    
    # Allocate linear table length and define linear table
    def __init__(self):
        self.MAX_SIZE = 7 #Generally, it is set to a space of 1000 to prevent overflow after insertion. This is for the convenience of later print inspection
        self.node = list()
        for i in range(self.MAX_SIZE):
            if i == self.MAX_SIZE-1:
                self.node.append(Node(0))
            else:
                self.node.append(Node(i+1))
    
    # Insert element at specified location
    def add(self, i, e):
        
        k = self.MAX_SIZE-1
        if i < 1 or i > self.ListLength()+1:
            return 0
        j = self.findEmpty()
        if (j):
            self.node[j].val = e
            for l in range(1,i):
                k = self.node[k].cur
            self.node[j].cur = self.node[k].cur
            self.node[k].cur = j
            return 1
        return 0
    
       # Find empty location
    def findEmpty(self):
        i = self.node[0].cur
        if (self.node[0].cur):
            self.node[0].cur = self.node[i].cur   
        return i  
    
    def ListLength(self):
        j = 0
        i = self.node[self.MAX_SIZE-1].cur
        
        while(i):
            i = self.node[i].cur
            j = j+1
        return j      
    
    # Deletes the element at the specified position in the linear table
    def remove(self, i):
        if i < 1 or i > self.ListLength():
            return 0
        k = self.MAX_SIZE-1
        for j in range(1,i):
            k = self.node[k].cur 
        j = self.node[k].cur
        self.node[k].cur = self.node[j].cur
        self.delempty(j)
    
    def delempty(self, k):
        self.node[k].cur = self.node[0].cur
        self.node[0].cur = k

    # Traversal by index
    def travel(self):
        for i in range(0, self.MAX_SIZE):
            print("i==", i, "; val==", self.node[i].val, "; cur==", self.node[i].cur)
ll = linkList()
ll.travel()
print("start")
ll.add(1, 'B')
ll.travel()
print("start")
ll.add(2, 'Bc')
ll.travel()
print("start")
ll.add(3, 'Bcd')
ll.travel()
print("del")
ll.remove(1)
ll.travel()
i== 0 ; val== None ; cur== 1
i== 1 ; val== None ; cur== 2
i== 2 ; val== None ; cur== 3
i== 3 ; val== None ; cur== 4
i== 4 ; val== None ; cur== 5
i== 5 ; val== None ; cur== 6
i== 6 ; val== None ; cur== 0
start
i== 0 ; val== None ; cur== 2
i== 1 ; val== B ; cur== 0
i== 2 ; val== None ; cur== 3
i== 3 ; val== None ; cur== 4
i== 4 ; val== None ; cur== 5
i== 5 ; val== None ; cur== 6
i== 6 ; val== None ; cur== 1
start
i== 0 ; val== None ; cur== 3
i== 1 ; val== B ; cur== 2
i== 2 ; val== Bc ; cur== 0
i== 3 ; val== None ; cur== 4
i== 4 ; val== None ; cur== 5
i== 5 ; val== None ; cur== 6
i== 6 ; val== None ; cur== 1
start
i== 0 ; val== None ; cur== 4
i== 1 ; val== B ; cur== 2
i== 2 ; val== Bc ; cur== 3
i== 3 ; val== Bcd ; cur== 0
i== 4 ; val== None ; cur== 5
i== 5 ; val== None ; cur== 6
i== 6 ; val== None ; cur== 1
del
i== 0 ; val== None ; cur== 1
i== 1 ; val== B ; cur== 4
i== 2 ; val== Bc ; cur== 3
i== 3 ; val== Bcd ; cur== 0
i== 4 ; val== None ; cur== 5
i== 5 ; val== None ; cur== 6
i== 6 ; val== None ; cur== 2

In general, the advantage of static linked list is that when inserting and deleting elements, we only need to change the cursor without moving the elements directly; The disadvantage is that it is still a static structure, and it loses a feature that the sequential storage structure can store randomly (the current index and cur do not correspond one-to-one). In essence, static linked list is only a method designed for high-level languages without pointers.

4. Two way linked list

Cited example:
When a high-speed railway starts in Shanghai and ends in Beijing, now we want to go from Beijing to Shanghai, but the linked list cannot be traversed in reverse.

Therefore, we add a prev pointing to the front-end node in a node, so that a node can point to both the front and the rear. This new form that can improve the comprehensive performance of the linked list is a two-way linked list.

#Bidirectional linked list
class Node(object):
    def __init__(self, elem):
        """Double linked list node"""
        self.elem = elem
        self.pre = None
        self.next = None


class DoubleLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def is_empty(self):
        """Determine whether the linked list is empty"""
        return self.__head is None

    def length(self):
        """Get linked list length"""
        cur = self.__head
        count = 0
        while cur is not None:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        """Traversal linked list"""
        cur = self.__head
        while cur is not None:
            print(cur.elem, end=' ')
            cur = cur.next
        print()

    def addFirst(self, elem):
        """Add element to double chain header"""
        node = Node(elem)
        if self.is_empty():  # The linked list is empty
            self.__head = node
        else:
            node.next = self.__head
            node.next.pre = node
            self.__head = node

    def addLast(self, elem):
        """Add a node to the end of the linked list"""
        node = Node(elem)
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
            cur.next = node
            node.pre = cur

    def add(self, pos, elem):
        """To linked list position pos Insert element at elem"""
        if pos == 0:
            self.addFirst(elem)
        elif pos > self.length() or pos < 0:
            raise ValueError('Add failed. Illegal index.')
        else:
            node = Node(elem)
            cur = self.__head
            pre = None
            for i in range(pos):
                pre = cur
                cur = cur.next
            pre.next = node
            node.pre = pre
            node.next = cur
            if cur:
                cur.pre = node

    def remove(self, elem):
        """Delete the first value in the linked list as elem Node of"""
        if self.is_empty():
            return
        cur = self.__head
        while cur is not None:
            if cur.elem == elem:
                if cur == self.__head:  # If it is a header node
                    self.__head = cur.next  # There is only one node in the linked list
                    if cur.next:
                        cur.next.pre = None
                else:
                    cur.pre.next = cur.next
                    if cur.next:  # If not the tail node
                        cur.next.pre = cur.pre
                break
            else:
                cur = cur.next

    def search(self, elem):
        """Find out whether there are elements in the linked list elem"""
        cur = self.__head
        while cur is not None:
            if cur.elem == elem:
                return True
            else:
                cur = cur.next
        return False
if __name__ == '__main__':
    double_linked_list = DoubleLinkedList()
    print(double_linked_list.is_empty())
    print('===================')
    double_linked_list.addLast(1)
    print('===================')
    double_linked_list.addLast(2)
    double_linked_list.addLast(3)

    double_linked_list.addFirst(7)

    double_linked_list.addLast(4)
    double_linked_list.addLast(5)

    double_linked_list.add(0, 13)  # 13, 7, 1, 2, 3, 4, 5
    double_linked_list.travel()

    double_linked_list.add(2, 99)  # 13, 7, 99, 1, 2, 3, 4, 5
    double_linked_list.travel()

    double_linked_list.add(8, 22)  # 13, 7, 99, 1, 2, 3, 4, 5, 22
    double_linked_list.travel()

    double_linked_list.remove(13)
    double_linked_list.travel()  # 7 99 1 2 3 4 5 22

    double_linked_list.remove(22)
    double_linked_list.travel()  # 7 99 1 2 3 4 5

    double_linked_list.remove(3)
    double_linked_list.travel()  # 7 99 1 2 4 5

    print(double_linked_list.search(100))  # False
    print(double_linked_list.search(7))  # True
    print(double_linked_list.search(2))  # True
    print(double_linked_list.search(5))  # True
True
===================
===================
13 7 1 2 3 4 5 
13 7 99 1 2 3 4 5 
13 7 99 1 2 3 4 5 22 
7 99 1 2 3 4 5 22 
7 99 1 2 3 4 5 
7 99 1 2 4 5 
False
True
True
True

5. One way circulation linked list

Cited example:
When a high-speed railway starts in Shanghai and ends in Beijing, now we want to go directly from Nanjing to Beijing, but the linked list requires us to traverse from the beginning, which is really inconvenient.

The next of the node at the end of a single linked list generally points to null. Now point it to the head node to make the whole linked list a ring. This linked list is called a one-way circular linked list.

This new form of linked list solves a problem: we can start from one node and then access all nodes of the linked list.

#One way circular linked list
class Node(object):
    def __init__(self, elem):
        """Single cycle linked list node"""
        self.elem = elem
        self.next = None


class SinglyLinkedCircularList(object):
    """One way circular linked list"""
    def __init__(self, node=None):
        self.__head = node

    def is_empty(self):
        """Determine whether the linked list is empty"""
        return self.__head is None

    def length(self):
        """Find the length of the linked list"""
        if self.is_empty():
            return 0
        cur = self.__head
        count = 1  # When the lower loop exits, the linked list has at least one node
        while cur.next != self.__head:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        """Traversal linked list"""
        if self.is_empty():
            return
        cur = self.__head
        while cur.next != self.__head:
            print(cur.elem, end=' ')
            cur = cur.next
        # When exiting the loop, cur points to the last node
        print(cur.elem)

    def addFirst(self, elem):
        """Add elements to the head of the linked list"""
        node = Node(elem)
        if self.is_empty():
            node.next = node
            self.__head = node
        else:
            node.next = self.__head
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            cur.next = node
            self.__head = node
           

    def addLast(self, elem):
        """Add elements to the end of the linked list"""
        node = Node(elem)
        if self.is_empty():
            node.next = node
            self.__head = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            node.next = self.__head
            cur.next = node

    def add(self, pos, elem):
        """To linked list position pos Insert element at

        Args:
            pos: Insert position, counting from 0
            elem: Inserted element
        """
        if pos <= 0:
            self.addFirst(elem)
        elif pos > (self.length() - 1):
            self.addLast(elem)
        else:
            pre = self.__head
            count = 0
            while count < (pos - 1):
                count += 1
                pre = pre.next
            # Exit the loop, and pre points to the previous node at the insertion position
            node = Node(elem)
            node.next = pre.next
            pre.next = node

    def remove(self, elem):
        """Delete the first in the linked list as elem Element of"""
        if self.is_empty():
            return
        cur = self.__head
        pre = None
        if cur.elem == elem:
            if cur.next != self.__head:
                while cur.next != self.__head:
                    cur = cur.next
                cur.next = self.__head.next
                self.__head = self.__head.next
            else:
                self.__head = None
        else:
            pre = self.__head
            while cur.next != self.__head:
                if cur.elem == elem:
                    pre.next = cur.next
                    return
                else:
                    pre = cur
                    cur = cur.next
            if cur.elem == elem:
                pre.next = cur.next
        

    def search(self, elem):
        """Find out whether there are elements in the linked list elem"""
        if self.is_empty():
            return False
        cur = self.__head
        while cur.next != self.__head:
            if cur.elem == elem:
                return True
            else:
                cur = cur.next
        # Exit the loop and cur points to the tail node
        if cur.elem == elem:
            return True
        return False
if __name__ == '__main__':
    singly_linked_circular_list = SinglyLinkedCircularList()
    print(singly_linked_circular_list.is_empty())
    print(singly_linked_circular_list.length())
    print('===================')

    singly_linked_circular_list.addLast(1) #1
    print(singly_linked_circular_list.is_empty())
    print(singly_linked_circular_list.length())
    print('===================')

    singly_linked_circular_list.addLast(2)#1,2
    singly_linked_circular_list.addLast(3)#1,2,3

    singly_linked_circular_list.addFirst(7)

    singly_linked_circular_list.addLast(4)
    singly_linked_circular_list.addLast(5) # 7, 1, 2, 3, 4, 5

    singly_linked_circular_list.add(0, 13)  # 13, 7, 1, 2, 3, 4, 5
    singly_linked_circular_list.travel()

    singly_linked_circular_list.add(2, 99)  # 13, 7, 99, 1, 2, 3, 4, 5
    singly_linked_circular_list.travel()

    singly_linked_circular_list.add(11, 22)  # 13, 7, 99, 1, 2, 3, 4, 5, 22
    singly_linked_circular_list.travel()

    singly_linked_circular_list.remove(13)
    singly_linked_circular_list.travel()  # 7 99 1 2 3 4 5 22

    singly_linked_circular_list.remove(22)
    singly_linked_circular_list.travel()  # 7 99 1 2 3 4 5

    singly_linked_circular_list.remove(3)
    singly_linked_circular_list.travel()  # 7 99 1 2 4 5

    print(singly_linked_circular_list.search(666))  # False
    print(singly_linked_circular_list.search(7))  # True
    print(singly_linked_circular_list.search(5))  # True
    print(singly_linked_circular_list.search(1))  # True
True
0
===================
False
1
===================
13 7 1 2 3 4 5
13 7 99 1 2 3 4 5
13 7 99 1 2 3 4 5 22
7 99 1 2 3 4 5 22
7 99 1 2 3 4 5
7 99 1 2 4 5
False
True
True
True

6. Bidirectional circular linked list

#Bidirectional circular linked list
class Node(object):
    def __init__(self, elem):
        """Double linked list node"""
        self.elem = elem  # This node value
        self.next = None   # Connect a node
        self.prev = None  # Previous node value


class DoubleCircleLinkedList(object):
    def __init__(self, node=None):
        self._head = node

    def is_empty(self):
        """Determine whether the linked list is empty"""
        return self._head is None

    def length(self):
        """Get linked list length"""
        if self.is_empty():
            return 0
        else:
            cur = self._head.next
            n = 1
            while cur != self._head:
                cur = cur.next
                n += 1
            return n

    def travel(self):
        """Traversal linked list"""
        cur = self._head
        while cur.next != self._head:
            print(cur.elem, end=' ')
            cur = cur.next
        print(cur.elem)
        
    def addFirst(self, elem):
        """Add element to double chain header"""
        node = Node(elem)
        if self.is_empty():  # The linked list is empty
            node.next = node
            node.prev = node
            self._head = node
        else:
            node.next = self._head
            node.prev = self._head.prev
            self._head.prev.next = node
            self._head.prev = node
            self._head = node

    def addLast(self, elem):
        """Add a node to the end of the linked list"""
        if self.is_empty():
            self.addFirst(elem)
        else:
            node = Node(elem)
            cur = self._head.next
            while cur.next != self._head:
                cur = cur.next
            cur.next = node
            node.prev = cur
            node.next = self._head
            self._head.prev = node

    def add(self, pos, elem):
        """To linked list position pos Insert element at elem"""
        if pos <= 0:
            self.addFirst(elem)
        elif pos >= self.length():
            self.addLast(elem)
        else:
            cur = self._head.next
            n = 1
            while cur.next != self._head:
                if n == pos:
                    break
                cur = cur.next
                n += 1
            node = Node(elem)
            node.prev = cur.prev
            cur.prev.next = node
            node.next = cur
            cur.prev = node

    def remove(self, elem):
        """Delete the first value in the linked list as elem Node of"""
        if self.is_empty():
            return
        else:
            if self._head.elem == elem:
                if self.length == 1:
                    self._head = Node
                else:
                    self._head.prev.next = self._head.next
                    self._head.next.prev = self._head.prev
                    self._head = self._head.next
            cur = self._head.next
            while cur != self._head:
                if cur.elem == elem:
                    cur.prev.next = cur.next
                    cur.next.prev = cur.prev
                cur = cur.next

    def search(self, elem):
        """Find out whether there are elements in the linked list elem"""
        if self.is_empty():
            return False
        else:
            cur = self._head.next
            if self._head.elem == elem:
                return True
            else:
                while cur != self._head:
                    if cur.elem == elem:
                        return True
                    else:
                        cur = cur.next
                return False
if __name__ == '__main__':
    double_linked_list = DoubleCircleLinkedList()
    double_linked_list.addLast(1)
    double_linked_list.addLast(2)
    double_linked_list.addLast(3)
    double_linked_list.addFirst(7)
    double_linked_list.addLast(4)
    double_linked_list.addLast(5)

    double_linked_list.add(0, 13)  # 13, 7, 1, 2, 3, 4, 5
    double_linked_list.travel()

    double_linked_list.add(2, 99)  # 13, 7, 99, 1, 2, 3, 4, 5
    double_linked_list.travel()

    double_linked_list.add(11, 22)  # 13, 7, 99, 1, 2, 3, 4, 5, 22
    double_linked_list.travel()

    double_linked_list.remove(13)
    double_linked_list.travel()  # 7 99 1 2 3 4 5 22

    double_linked_list.remove(22)
    double_linked_list.travel()  # 7 99 1 2 3 4 5

    double_linked_list.remove(3)
    double_linked_list.travel()  # 7 99 1 2 4 5

    print(double_linked_list.search(100))  # False
    print(double_linked_list.search(7))  # True
    print(double_linked_list.search(2))  # True
    print(double_linked_list.search(5))  # True
13 7 1 2 3 4 5
13 7 99 1 2 3 4 5
13 7 99 1 2 3 4 5 22
7 99 1 2 3 4 5 22
7 99 1 2 3 4 5
7 99 1 2 4 5
False
True
True
True

Tags: Python Algorithm data structure linked list Singly Linked List

Posted on Fri, 24 Sep 2021 08:04:08 -0400 by activeserver