Algorithm review (11): return to the linked list of the basic data structure

1. Write on the front

This article is a review of the linked list. The operation of the linked list here is generally to play with pointers. Double pointers and three pointers go hand in hand. Fast and slow pointers break the convention. The key to solving the problem here is nothing else. First draw a picture, and then find the corresponding pointer to perform the transformation operation. . The place where bugs are easy to cause is out of bounds, or the pointer forgets to move and falls into an infinite loop. The following is to sort out a few common operations related to the linked list, and then sort out the specific questions and codes, and finally only sort out the questions, so that you can use it later. Start below. Basic knowledge reference

About the linked list, we need to know the knowledge points:

  1. Common operations: add, delete, modify, and check elements, the more complicated ones are all kinds of pointers

  2. Linklist access array time complexity O(n), insert and delete time complexity O(1), memory is not continuous

  3. Some of the more classic topics examine the pre-interpolation, post-interpolation, reverse order, flip, merge, and move of the linked list .

  4. Some ideas involved here:

    • The head node idea , declaring a head node can be convenient for many things. It is generally used in the problem of returning a new linked list. For example, given two ordered linked lists, they are required to be integrated and arranged in order. For another example, if the odd and even numbers in a linked list are separated in the original order and then recombined into a new linked list, the first half of the linked list is odd numbers, and the second half is even numbers.
    • Head inversion thought
    • Double pointer traversal , you can do many things, such as pairwise swap, reverse order, flip, etc.
    • The idea of fast and slow pointers can generally be used in the ring
    • Recursion, the topic of the linked list is easy to recurse
  5. Advantages and disadvantages:

    1. Advantages: linked list can flexibly allocate memory space; can be in O (1) O(1)Delete or add an element within O ( 1 ) time, provided that the previous element of the element is known, of course it also depends on whether it is a singly-linked list or a double-linked list. In a double-linked list, if the next element of the element is known, it can also beO (1) O (1)Delete or add the element within O ( 1 ) time.
    2. Disadvantages: Unlike arrays that can quickly read elements through subscripts, you must read one by one from the head of the linked list each time; query kkk elements requireO (k) O(k)O ( k ) time.
  6. Application scenario: If the problem to be solved requires a lot of quick queries, the linked list may not be suitable; if the problem encountered, the number of data elements is uncertain, and data needs to be added and deleted frequently, then the linked list will be more suitable . And if the size of the data element is determined and there are not many delete and insert operations, then the array may be more suitable.

  7. Suggestion: When solving the problem of the linked list, you can draw the relationship between the nodes on paper or whiteboard, and then draw the method of modification, which can help you analyze the problem, and help the interviewer clear during the interview. To see your thoughts.

Regarding linked lists, you should first master several routines: lookup, insert, and delete operations.

You need to know how to define the linked list node in python. Generally, you don't need to write this part in LeetCode, but you will not give it during a real interview. You need to write it yourself.

class ListNode:
	def __init__(self, val=0, next=None):
		self.val = val
		self.next = next

The following is the general operation of the linked list, directly take a question to organize:

  • LeetCode707: Designing a linked list : This question directly examines the basic operations of the linked list, searching for nodes to return to the index, inserting at the first position, inserting in the middle, inserting at the end, deleting nodes, etc.

This problem first needs to establish a linked list node, the above code, and then initialize a head node

class MyLinkedList:
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.dummyHead = ListNode(-1)

The lookup operation of the linked list, given the index, return the value of the corresponding index

def get(self, index: int) -> int:
    """
    Get the value of the index-th node in the linked list. If the index is invalid, return -1.
    """
    if index < 0: return -1
    p = self.dummyHead.next
    cou = 0
    while p and cou < index:
        p = p.next
        cou += 1
    # self.printl()   打印当前链表结果
    return p.val if p else -1

Insert at the first position of the linked list:

def addAtHead(self, val: int) -> None:
    """
    Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
    """
    node = ListNode(val)
    node.next = self.dummyHead.next
    self.dummyHead.next = node
    # self.printl()   # 打印当前链表结果

Insert at the end of the linked list:

def addAtTail(self, val: int) -> None:
    """
    Append a node of value val to the last element of the linked list.
    """
    node = ListNode(val)
    # 首先到尾部
    pre, cur = self.dummyHead, self.dummyHead.next
    while cur:
        pre, cur = cur, cur.next
    pre.next = node
    # self.printl()   # 打印当前链表结果

Insert at the specified position of the linked list:

def addAtIndex(self, index: int, val: int) -> None:
    """
    Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
    """
    node = ListNode(val)
    cou = 0
    pre, cur = self.dummyHead, self.dummyHead.next
    while cur and cou < index:
        pre, cur = cur, cur.next
        cou += 1
    # 插入
    node.next = cur
    pre.next = node
    # self.printl()   打印当前链表结果

Delete operation of linked list:

def deleteAtIndex(self, index: int) -> None:
    """
    Delete the index-th node in the linked list, if the index is valid.
    """
    cou = 0
    pre, cur = self.dummyHead, self.dummyHead.next
    while cur and cou < index:
        pre, cur = cur, cur.next
        cou += 1
    # 删除
    pre.next = cur.next if cur else None
    # self.printl()   打印当前链表结果

For debugging, I also wrote a function to output the elements of the linked list, because when I submitted it for the first time, there was an error, and I don’t know which step was the problem, so I wrote a function like this, after each step of the operation , Output it, and the error appears instantly.

def printl(self):
    p = self.dummyHead.next
    while p:
        print(p.val, end=',')
        p = p.next
    print()

Regarding the basic code, there are two more operations, namely forward insertion and tail insertion to build a linked list. This will be met in the understanding of specific topics. The front interpolation will get a reversed linked list, and the tail interpolation will get a normal linked list.

Look at the specific topic below.

2. Question ideas and code sorting

2.1 Deletion of linked list elements

  • LeetCode203: Remove linked list elements : The most basic problem of linked list operations, to examine the deletion of linked lists. At this time, the two pointers pre and cur are traversed. Pre is responsible for pointing to the previous node of cur, and is responsible for deleting elements after finding, and cur is responsible for finding the target element. Declaring the head node will make the operation easier.
    Insert picture description here
  • LeetCode19: Delete the Kth node of the linked list : This needs to use the fast and slow pointersfast, slow , let fastgo n steps first, and then fast and slow go hand in hand. When it fastcomes to the end, it slowis the nth node from the bottom , but here we want to delete the nth node from the bottom Node, so while advancing, there must be a node in front of the slow record. In order to unify the deletion of the first one and the middle one, a head node is needed here.
    Insert picture description here

2.2 Reversal of linked list

  • LeetCode206: Reverse Linked List : There are three ways of this, and they are all very basic, I want to sort them out here.

    1. First of all, pre, curthese two buddies are really easy to use. When traversing forward, the pointer is adjusted by the way. The code for pointer adjustment in python can use tuple unpacking to be more concise, but before writing this, it is best to Write the prototype, otherwise it is easy to make mistakes:
      Insert picture description here
      in fact , it is very simple to write the above code according to the prototype. The left side of the equal sign in the prototype is placed on the left in order, and the right side of the equal sign in the prototype is placed on the right in order. But the prototype must be written correctly, this is related to the order of manipulation.
    2. The idea of ​​rebuilding the array with the head interpolation: As mentioned above, the head interpolation can create a linked list in reverse order.
      Insert picture description here
    3. Recursive thinking: This question can be tail recursive. For the current incoming node, if I get the reverse order of the nodes behind it, that is, if I have head->nextall the reverse order, do I only need to connect the head to the end? Just face it. Based on this idea, tail recursion can be used.

      Insert picture description here
  • LeetCode25: K a group of flipped linked lists : This question needs to use the idea of ​​inverting linked lists, but here is not to reverse all, but to reverse one group, so first write the above linked list inversion as a function to realize the inversion from [a, b)Node. Then it can be reversed, and here is the idea of ​​rebuilding the linked list by inserting K nodes at the end at a time. Use two pointers k_start, k_end, k_endgo kk firstStep k , and then reverse[k_start, k_end)the node, insert the tail of the reversed node into the new linked list (the way of tail insertion). Then continue to perform the above process, when it is found that the latter is not enoughkkIf there are k , insert the remaining direct tails into the new linked list. The key here is to reverse the link list node and tail insertion idea at a given position.
    Insert picture description here

  • LeetCode61: Rotating linked list : I have played with rotating arrays before. Here is the selection of linked lists. The idea is actually the same as that of arrays. First, reverse the overall order, then reverse the first k, and then reverse the k after it. At that time, the operation of the linked list could not be so straightforward. When the order is reversed, the above reverse function is very important. It is still the tail interpolation idea + reverse interval node operation
    Insert picture description here

2.3 Exchange of linked list nodes

  • Swap the nodes in the linked list in pairs : This topic is exchange again, and the exchange is similar to the reverse order, but this is simpler, it is the reverse order of adjacent nodes, the idea of ​​double pointer + tail insertion to build a linked list .
    Insert picture description here

2.4 Circular linked list

  • Circular linked list : The best tool to find the ring in the linked list is the speed pointer. Define two pointers fast, slow. Each time you slowgo one step fastdown and two steps down. If they meet at a certain moment, it means there is ring.

    Insert picture description here
  • Circular linked list II : This question is directly on the idea. To find the entrance of the ring, first find the meeting point. After that, slow returns to the starting point and fast stays at the meeting point. The two go forward step by step. When they meet again , Is the entrance of the ring. why? At the beginning, this is a calculation problem of distance. Assuming that when the first encounter, slow takes K steps, then fast takes 2K steps (the speed is twice as slow), then it is equivalent to slow walking from the starting point to the encounter The distance of the point is equal to the distance of fast from the meeting point, turning a circle back to the meeting point , and the starting point is nothing more than before the meeting point. For example, if you step back from the meeting point to the starting point, then the head is away from the starting point. It is the same as the distance from the meeting point to the starting point, which is Km steps. So the two meet first, then slow back to the starting point, and then walk synchronously, and then meet is the starting point.

Insert picture description here

Of course, the two loops found above can also be easily removed with a hash table. Take the second loop-finding entry, only one pointer is needed to traverse the linked list. Every time a node is traversed, it will be saved. Into the set collection. When it is found that a certain node has been traversed, there is already this node in the hash table, indicating that this is the entry of the ring, just return to this node. Similarly, it can also be judged that there is a ring at this time.
Insert picture description here

  • LeetCode876: The middle node of the linked list : the classic problem of fast and slow pointers. Both start from the beginning at the same time. Fast takes two steps at a time and slow takes one step at a time, but when fast goes to the end, slow is the middle node at this time.
    Insert picture description here

2.5 Linked List Merging

3. Mr. General

There are relatively few questions on the linked list, so I took a day to review it again. Some basic ideas are very important, and drawing is also very important. Here is the standard configuration that I often use in the linked list:

  1. Head nodedummyHead
  2. pre, curTwo pointers
  3. fast, slowTwo pointers
  4. Tail plug + double pointer, tail plug + head plug

Now the standard configuration of double pointers:
5. pre, curTwo pointers (forward and backward pointers —> insertion, deletion, and reversal of linked list nodes)
6. fast, slowTwo pointers (fast and slow pointer —> judging the ring, find the midpoint of the linked list, find the Nth from the bottom Node)
7. left, rightTwo pointers (left and right pointers -> array, string related topics)
8. win_start, win_endTwo pointers (sliding window -> subarray, substring related topics)

Important thoughts in this piece:

  • Tail plug + double pointer
  • Tail Insert + Reverse Interval Linked List Node
  • Tail plug + head plug

The final topic is summarized as follows:

Deletion of linked list elements:

Reversal of linked list

Exchange of linked list nodes

Circular linked list

Linked list merge

Guess you like

Origin blog.csdn.net/wuzhongqiang/article/details/115266111