# 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.1 basic 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__()

def __init__(self):
self._size = 0

def get_size(self):
return self._size

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

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. ```def add_first(self, e):
node = self._Node(e)
self._size += 1
```

The implementation of tail addition is similar to queue:

```def add_last(self, e):
node = self._Node(e)
self._size += 1
return
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:
if index == 0:
return
for i in range(index-1):
prev = prev.next
node = self._Node(e)
node.next = prev.next
prev.next = node
self._size += 1
```

```def add_first(self, 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):
data = []
while curr:
data.append(str(curr.e))
curr = curr.next
' -> '.join(data) + ' (Tail)'

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

Check it out!

```if __name__ == '__main__':
```
```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._size = 0
'''
def __init__(self):
self._size = 0

def get_size(self):
return self._size

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

if index < 0 or index > self._size:
'''
if index == 0:
return
'''
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):
'''
'''
data = []
while curr:
data.append(str(curr.e))
curr = curr.next
' -> '.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.')
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.')
for i in range(index):
curr = curr.next
curr.e = e

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

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

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

Inspection:

```if __name__ == '__main__':

```
```0
4
1
5
1
```

## 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.')
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):
data = []
while curr:
data.append(str(curr.e))
curr = curr.next
' -> '.join(data) + ' (Tail)'

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

Inspection:

```if __name__ == '__main__':

```
```0
```

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

# 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

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.cur
if (self.node.cur):
self.node.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.cur
self.node.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.travel()
print("start")
ll.travel()
print("start")
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):
self.elem = elem
self.pre = None
self.next = None

def __init__(self, node=None):

def is_empty(self):
"""Determine whether the linked list is empty"""

def length(self):
count = 0
while cur is not None:
count += 1
cur = cur.next
return count

def travel(self):
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
print()

node = Node(elem)
if self.is_empty():  # The linked list is empty
else:
node.next.pre = node

node = Node(elem)
if self.is_empty():
else:
while cur.next is not None:
cur = cur.next
cur.next = node
node.pre = cur

"""To linked list position pos Insert element at elem"""
if pos == 0:
elif pos > self.length() or pos < 0:
else:
node = Node(elem)
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
while cur is not None:
if cur.elem == elem:
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"""
while cur is not None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
```
```if __name__ == '__main__':
print('===================')
print('===================')

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

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

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

double_linked_list.travel()  # 7 99 1 2 4 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
```

# 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):
self.elem = elem
self.next = None

def __init__(self, node=None):

def is_empty(self):
"""Determine whether the linked list is empty"""

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

def travel(self):
if self.is_empty():
return
print(cur.elem, end=' ')
cur = cur.next
# When exiting the loop, cur points to the last node
print(cur.elem)

node = Node(elem)
if self.is_empty():
node.next = node
else:
cur = cur.next
cur.next = node

node = Node(elem)
if self.is_empty():
node.next = node
else:
cur = cur.next
cur.next = node

"""To linked list position pos Insert element at

Args:
pos: Insert position, counting from 0
elem: Inserted element
"""
if pos <= 0:
elif pos > (self.length() - 1):
else:
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
pre = None
if cur.elem == elem:
cur = cur.next
else:
else:
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
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__':
print('===================')

print('===================')

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

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

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

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

```
```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):
self.elem = elem  # This node value
self.next = None   # Connect a node
self.prev = None  # Previous node value

def __init__(self, node=None):

def is_empty(self):
"""Determine whether the linked list is empty"""

def length(self):
if self.is_empty():
return 0
else:
n = 1
cur = cur.next
n += 1
return n

def travel(self):
print(cur.elem, end=' ')
cur = cur.next
print(cur.elem)

node = Node(elem)
if self.is_empty():  # The linked list is empty
node.next = node
node.prev = node
else:

if self.is_empty():
else:
node = Node(elem)
cur = cur.next
cur.next = node
node.prev = cur

"""To linked list position pos Insert element at elem"""
if pos <= 0:
elif pos >= self.length():
else:
n = 1
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.length == 1:
else:
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:
return True
else:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
```
```if __name__ == '__main__':

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

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

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

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

```
```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
```

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