[Python data structure] - chain structure

Keywords: Python data structure linked list

Python data structure @ EnzoReventon

Python data structure - linked structure LinkNode - single linked list

catalog:

1. Single linked list
1.1 first initialize a root node to represent the first location
1.2 define a method to add nodes at the tail
1.3 add a new node at the head of the linked list
1.4 iterators
1.5 find the content specified in the linked list
1.6 find the number of duplicate contents in the linked list
1.7 delete a node content
1.8 delete all the same contents specified in the linked list

2. Function test

======================================================================================

In this paper, we mainly introduce two chain structures, single linked list and double linked list.
As the name suggests, a single linked list is one-way, a double linked list is two-way, and a double linked list can be traversed in reverse.
Why introduce the concept of linked list?
For the above Linear structure For the Array array introduced in, it is very troublesome to insert data in the head or middle using list. You need to translate the data at the insertion position one by one to the back.
In particular, it is not appropriate to frequently insert or delete data at the head and tail.

Linked list is a relatively winding and complex thing, which also shows some of its shortcomings. For example, it can not quickly access the data at a certain location by specifying index like an array, but needs to be traversed one by one from beginning to end.
In the process of learning, the focus is to understand how to establish links between linked lists. If you clarify all kinds of relationships, the code will be easy to write.

1 single linked list

First, let's introduce the single linked list. The data stored in the linked list is discontinuous. It "strings" many data, which is different from the array. The data in the array is a simple data, but the linked list is different. The data is only a part of it. The upper level is called a node. A node contains data + pointer. The pointer here is similar to the pointer in C language, The effect is similar, but the usage is different. After careful study, readers will find its characteristics; To get down to business, the pointer here points to a node. For a single linked list, because the single linked list is single, the pointer often points to the next node.


The initialization node code is as follows:

class Node():		#Define a node class and initialize the class. The class contains value and next
    def __init__(self,value,next=None):		#Define an initialization method
        self.value = value					#Method defines value to store data and Next to point to the Next node
        self.next = next

So how are linked lists related?
As shown in the following figure, the Next pointer of node 1 points to node 2, the pointer of node 2 points to node 3, and so on.

Of course, the pointer of node 4 can also point to node 1 to form a circular list.
For example, realize the association of nodes.

node1 =  Node(value1)
node2 =  Node(value2)
node1.next = node2 

In this way, the association of linked list nodes can be realized. Does it look very simple?

Of course, for a linked list, the association of nodes is only a small part. For a linked list, our more operations are to add nodes, delete nodes, traverse nodes and so on.
Next, I will introduce in detail how to insert nodes at the tail, insert nodes at the head, traverse nodes, find nodes, find the number of duplicate data, remove nodes and remove all nodes.

1.1 first initialize a root node to represent the first location

class LinkedList(object):  			#A class that defines a linked list
    def __init__(self,root=None):	#Define the initialization method of the linked list
        self.root = Node()			#Instantiate a root node with a value of None
        self.size = 0				#Record the size of the linked list and how many nodes it contains
        self.next = None 			#Define a Next pointer. When adding data, it is associated with the new data. It has not been initialized yet, so it is None

1.2 define a method to add nodes at the tail

For adding a new node at the tail, we need to consider whether there are nodes in the linked list, that is, whether I add a new node after the root node.
If the linked list is still empty and there is only one root node, we can hang the new node behind root. As shown in the figure below.

If the linked list already has nodes, add data after the last node of the linked list.

The program is implemented as follows:

class LinkedList(object):
    def __init__(self,root=None):#Initialization method
        self.root = Node()
        self.size = 0
        self.next = None #When adding new data, who is associated with the address of the new data

    def add(self,value):
        node = Node(value)
        #Judge whether there is data
        if not self.next:
            self.root.next = node #After hanging the new node to root
        else:
            self.next.next = node
        self.next = node

First, create a LinkList linked list class.

  • Under this class, first define an initialization method, in which first create a root node.
  • self.root = Node() instantiates this root node.
  • self.next = None initializes the root node whose address and content are None.
  • self.size = 0 records the number of nodes in the linked list.

Secondly, define an add node function.

  • node = Node(value) means to instantiate the new data as a new node and automatically specify the memory.
  • if not self.next: judge whether the linked list has only one root or whether there are nodes in the linked list.
  • self.root.next = node connects the new node to the root node. The current self has no storage address, which is empty. It can be understood as a pointer to root.
  • else: after that, self.next.next = node adds the new node to the last node. Self stores the address of the last node before adding the new node. Self.next.next refers to the next of the last node before adding the node. If the reader doesn't understand, you can see the judgment part of this function: if not self.next: this sentence is judgment, If there is a node behind the root of the linked list, self.next can be understood as always pointing to the last node, and the next of the last node must point to the newly added node. Ps: it can be understood as a pointer that always points to the last node.
  • self.next = node is to update the last node, pointing to the newly added node.

My classmates and I spent an hour and a half to understand this part. The experiment proved it from the perspective of self memory change, self.next memory change and stack. Finally, we came to the conclusion that self stores an address, something similar to a pointer, and always points to the last node. It also makes us more deeply understand the meaning of object-oriented. Thank you for my good brother @ZoomToday , welcome to visit.

Once you understand the first one, the other functions will be solved easily!

1.3 add a new node at the head of the linked list

Similarly, before adding, we need to judge whether the linked list is empty. If it is empty, we can add it directly after root.
Otherwise, we need to disconnect the first node associated with root from root, and then associate the new node with root and with the node just disconnected.
As shown in the figure below:

The program is implemented as follows:

    def add_first(self, value):
        node = Node(value)
        if not self.next:
            self.root.next = node
            self.next = node
        else:
            temp = self.root.next  # Get the node behind the original root
            self.root.next = node  # Hang the new node to root
            node.next = temp       # The next node of the new node is the node after the original root
        self.size += 1

In this program, we should pay special attention to the following parts of else. The previous parts are the same as the add function, so we won't repeat them here.

  • temp = self.root.next. Here, you need to introduce a temp as an intermediate variable to save the node behind root.
  • self.root.next = node associates root.next with the new node chain.
  • node.next = temp associates the next of the newly added node with the newly saved temp, that is, the original first node.
  • self.size += 1 link size+1
    It can also be well proved from the side of this code that self.next always points to the last node of the linked list, because the first part of else of this code operates on the tail of the linked list, and it is necessary to update the tail node, that is, update self.next = node. After else, our operation is not at the end of the linked list, but at the head, so we don't use self.next or update it.

1.4 iterators

It is used to traverse the linked list later. The overall idea is to traverse from the first node after root until it reaches self.next.
The code is as follows:

    def __iter__(self):             #ergodic
        current = self.root.next
        if current:
            while current is not self.next:
                yield current
                current = current.next
            yield current

Continuously output the traversal node content to facilitate the later use of find, remove and other methods.

1.5 find the content specified in the linked list

This is relatively simple. You only need to use the iterator to compare the content you need to find with the output of the iterator. If it is equal, it will output True.

    def find(self, value):          #Find the specified element
        for v in self.__iter__():
            if v.value == value:
                return True

Not applicable__ iter__ The search method of iterator is as follows. It returns the address. The overall idea is the same:

    def find2(self, value):
        current = self.root.next
        if current:
            while current is not self.next:
                if current.value == value:
                    return current
                current = current.next

1.6 find the number of duplicate contents in the linked list

It is the same as searching. You can introduce a counter + + and do not exit in case of the same.

    def find_count(self,value):  #How many search data
        count = 0
        for n in self.__iter__():
            if n.value == value:
                count += 1
        return count

1.7 delete a node content

To delete the content of a node, we first need to traverse to find the corresponding node, and then judge whether this node is the last node. If the last node is to be deleted, we need to set the next of its previous node to None; If it is not the last node, we need to associate the next of the previous node of the node to be deleted with the next node to be deleted. As shown in the figure below:

  • Is the last node
  • Not the last node

Code implementation:

    def remove(self, value):
        prev = self.root
        for n in self.__iter__():
            if n.value == value:
                if n == self.next:
                    prev.next = None
                    self.next = prev
                prev.next = n.next
                del n
                self.size -= 1
                return True
            prev = n

There are several considerations in this Code:

  • prev = self.root records the previous node of the current node
  • for n in self.__iter__(): Traverse back
  • if n.value == value: judge whether the traversed content is the same as the content you need to delete
  • if n == self.next: if the same node is found, judge whether it is the last node
  • prev.next = None / self.next = prev if it is the last node, assign the next of this node to None. Similarly, self.next needs to point to the last node.
  • prev.next = n.next if it is not the last node, then the next of the previous node is equal to the next of this node, that is, the previous node is linked to the next node, del n deletes this node, self.size -= 1, total - 1.
  • prev = n update the previous node at the end of each traversal. To facilitate traversal

1.8 delete all the same contents specified in the linked list

The idea is the same as deleting the specified content, but one more loop for complete traversal.

    def remove_all(self,value):
        prev = self.root
        for n in self.__iter__():
            if n.value == value:
                if n == self.next:
                    prev.next = None
                    self.next = prev
                prev.next = n.next
                del n
                self.size -= 1
                continue
            prev = n

The difference is that after continue deletes the node, it continues to cycle until the traversal ends, while the above remove deletes the specified content and return s.

2 function test

.add(value) .add_first(value)

if __name__ == '__main__':
    link = LinkedList()
    link.add("111111")
    link.add_first("222222")
    link.add("111111")
    link.add("111111")
    link.add("111111")
    link.add("111111")


    for v in link: #Traversal linked list
        print(v)

Results: I added five "111111" and one "22222" in the head. As shown in the figure below:

.find(value)

if __name__ == '__main__':
    link = LinkedList()
    link.add("111111")
    link.add_first("222222")
    link.add("111111")
    link.add("111111")
    link.add("111111")
    link.add("111111")
    print(link.find("222222"))

Results: "22222" was found in so many linked lists

.find_count(value)

if __name__ == '__main__':
    link = LinkedList()
    link.add('11111')
    link.add('22222')
    link.add('11111')
    print(link.find_count('11111'))
    print(link.find_count('22222'))

result:

.remove(value)

if __name__ == '__main__':
    link = LinkedList()
    link.add('11111')
    link.add('22222')
    link.add('11111')


    link.remove('11111')

    print(link.find_count('11111'))
    for i in link:
        print(i.value)

Result: delete one 11111 and print out one 11111.

.remove_all(value)

if __name__ == '__main__':
    link = LinkedList()
    link.add('11111')
    link.add('22222')
    link.add('11111')


    link.remove_all('11111')

    print(link.find_count('11111'))
    for i in link:
        print(i.value)

Results: all 11111 were deleted, 0 remained, and one 22222 was printed

Posted by sajidfiaz on Sun, 05 Sep 2021 20:03:56 -0700