[data structure and algorithm] LeetCode single linked list exercise

Posted by quetz67 on Thu, 30 Dec 2021 11:13:37 +0100

Reverse linked list

Title link here

Three pointers:

Problem solving ideas:

  1. First analyze the special case. When the linked list is empty or there is only one node, directly return to head

  2. When there are more than one node in the linked list, we first consider whether we can reverse the pointer in turn, so as to reverse the linked list, as shown in the following figure:

    Then, to reverse the pointer, you can first define two pointers n1 and n2

    n2->next=n1;
    

    But there is a problem here. When n2 points to n1, the address of the second node is lost. Therefore, it is necessary to define an additional pointer n3 to save the address of the second node

  3. When the first node points to NULL, n1, n2 and n3 move one node backward to make the second node point to the first node

    Here, the movement of the three pointers is completed by iteration, which is divided into three steps:

    1. n1=n2
    2. n2=n3
    3. n3=n3->next
  4. Repeat the third step until n2 points to NULL, the cycle ends and the reverse linked list is completed

    It should be noted here that since the inversion is performed now and the iteration is in progress, when n3 points to NULL, the loop will not end until an iteration is performed

    Complete code
    	struct ListNode* reverseList(struct ListNode* head){
        if(head==NULL||head->next==NULL) 
            return head;
    
        struct ListNode* n1=NULL,*n2=head,*n3=head->next;
        while(n2)
        {
            n2->next=n1;
            n1=n2;
            n2=n3;
            if(n3)
                n3=n3->next;
    
        }
        return n1;
    }
    

Head insertion method:

Problem solving ideas:

  1. In addition to reversing from the original linked list, you can also open up a new linked list with the pointer newhead pointing to the header Head insertion , insert the nodes of the original linked list into the new linked list in turn

    struct ListNode* newhead = NULL;
    
  2. First define two pointers cur and next. Cur is used to take the node from the old linked list and insert it into the new linked list. Next is the same as saving the next node, so as to prevent cur from finding the old linked list again after moving the node to the new linked list

  3. When cur==NULL, the linked list is reversed. The specific process is as follows:

    Complete code
    struct ListNode* reverseList(struct ListNode* head){
        struct ListNode* newhead = NULL;       //Open up a new linked list
        struct ListNode* cur = head;           //cur is used to take the knot from the old linked list and insert it into the new linked list
    
        while(cur)			//When cur is not NULL, the loop continues
        {
            struct ListNode* next=cur->next;   //It is used to save the next node of cur mobile node
    
            cur->next=newhead;					//Insert the header into the new linked list
            newhead=cur;						//newNode points to the new header node in the new linked list
    
            cur=next;                           //cur returns to the old linked list and continues to move the next node
        }
        return newhead;
    }
    

Intermediate node of linked list

Title link here

Solution: speed pointer

  1. According to the meaning of the topic, after traversing the linked list, you need to return to the intermediate node
  2. Then whether two pointers can be defined. When the fast pointer traverses, the slow pointer just points to the middle node of the linked list

  1. It is not difficult to find that as long as the fast pointer takes one more step each time than the slow pointer, at the end, the slow pointer can point to the middle node of the linked list
Complete code
struct ListNode* slow=head;
struct ListNode* fast=head;  //The start positions of the fast and slow pointers point to the head node

//When fast==NULL (the number of nodes is odd), fast - > next = = null (the number of nodes is even), the cycle ends and intermediate nodes are found
while(fast&&fast->next)  
{
    slow = slow->next;		
    fast = fast->next->next;  //The slow pointer takes one step and the fast pointer takes two steps
}
return slow;

The penultimate node in the lin k ed list

Title: enter a linked list and output the penultimate node in the linked list. In order to meet the habit of most people, this question starts from 1, that is, the tail node of the linked list is the penultimate node. For example, a linked list has 6 nodes. Starting from the beginning, their values are 1, 2, 3, 4, 5 and 6. The penultimate node of the linked list is the node with the value of 4.

Problem solving ideas:

  1. Like the previous question, this question also requires to return a node in the linked list. Therefore, it can also be solved by using fast and slow pointers
  2. Define the fast and slow pointer, but the difference here is that the starting position of the fast pointer points to NULL, the slow pointer points to the head node, the fast pointer takes k steps in advance, and then the fast and slow pointer moves forward at the same time, so the fast pointer will always lead the slow pointer by k nodes. After the fast pointer traverses the linked list, the node pointed to by the slow pointer is the penultimate node in the linked list.
struct ListNode*fast=NULL; 	//The fast pointer points to NULL
struct ListNode*slow=pHead;  //The slow pointer points to the head node
while(k--)		//Go k steps first
{
    if(fast)
     	fast=fast->next;
    else
        return NULL;
}
while(fast)   //The fast and slow pointers move at the same time
{
    fast=fast->next;
    slow=slow->next;
}
return slow;

Merge two ordered linked lists

Title link here

  1. First, consider the special case. If all the linked lists are empty, return one of them. If one of them is empty, return another non empty linked list

    	if(list1==NULL)
        {
            return list2;
        }
        if(list2==NULL)
        {
            return list1;
        }
    
  2. In addition to the first special case, other cases can malloc a head node with a sentinel position, and compare the node sizes of list1 and list2 in turn. The small node has priority to be tail inserted into the new linked list. To carry out tail insertion, it is necessary to define a pointer to the tail node of the new linked list and record the address of the tail node

    	Node*head=NULL,*tail=NULL;
    	head=tail=(Node*)malloc(sizeof(Node));
    
    	//Take the small tail insert
        while(list1&&list2)
        {
            if(list1->val<list2->val)
            {
                tail->next=list1;
                list1=list1->next;
            }
            else
            {
                tail->next=list2;
                list2=list2->next;
            }
            tail=tail->next;
        }
    
  3. When does it end? When one of the linked lists is empty, insert the whole tail of the rest of the other linked list after the new linked list

    	if(list1)
            tail->next=list1;
        else
            tail->next=list2;
    

The overall process is as follows:

Complete code
typedef struct ListNode Node;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    if(list1==NULL) //Judgment of special circumstances
    {
        return list2;
    }
    if(list2==NULL)
    {
        return list1;
    }
    Node*head=NULL,*tail=NULL;
    //The head node with sentinel position does not store valid data
    head=tail=(Node*)malloc(sizeof(Node));
    //Take the small tail insert
    while(list1&&list2)  //Both linked lists are not empty
    {
        if(list1->val<list2->val)
        {
            tail->next=list1;
            list1=list1->next;
        }
        else
        {
            tail->next=list2;
            list2=list2->next;
        }
        tail=tail->next;
    }
    if(list1)
        tail->next=list1;
    else
        tail->next=list2;
    
    Node *realHead;		
    realHead=head->next;	//The real head node is the next node of the sentry position
    return realHead;
}

Linked list segmentation

Title link here

Idea:

Since the linked list cannot be reordered on the basis of the original linked list, since the topic requires that the larger than X be moved to node X and the smaller than X be moved to node x, we can open up two linked lists, one to store the smaller than x node (smallhead) and the other to store the larger than x node (bighead), and then connect the two linked lists.

  1. Traverse the original linked list, insert the end of the node smaller than x into smallhead and the head larger than x into bighead, and finally get the following two linked lists.

  1. It should be noted here that since the head node of the developed linked list is a sentinel node and does not store data, when two linked lists are linked, the last node of the sentinel bit head node of the large linked list should be linked with the tail node of the small linked list, and the tail node of the large linked list should be empty, so as to get the separated linked list.
smalltail->next=bigHead->next;
bigtail->next=NULL;

Complete code
typedef struct ListNode Node; 
struct ListNode* partition(struct ListNode* head, int x){
Node *smallHead,*smalltail,*bigHead,*bigtail;
smallHead=smalltail=(Node*)malloc(sizeof(Node));
bigHead=bigtail=(Node*)malloc(sizeof(Node));
smallHead->next=smalltail->next=NULL;
bigHead->next=bigtail->next=NULL;


while(head)
{
    if(head->val<x)
    {
        smalltail->next=head;
        smalltail=smalltail->next;
    }
    else 
    {
        bigtail->next=head;
        bigtail=bigtail->next;
    }
    head=head->next;
}
    smalltail->next=bigHead->next;
    bigtail->next=NULL;
    return smallHead->next;
}

Palindrome linked list

Title link here

Idea:

  1. To judge whether the linked list is a palindrome linked list, we can disconnect from the intermediate node and compare each node one by one. How to find the intermediate node can still use the fast and slow pointer method. Each step of the slow pointer, the fast pointer takes two steps. When the fast pointer reaches the end, the slow pointer points to the intermediate node, but we don't want to disconnect after the intermediate node, But to disconnect the previous node of the intermediate node, we also need a pointer to keep the previous node of the slow pointer.

  2. However, because the linked list cannot be traversed in reverse, the second linked list must be reversed. The inversion method is the same as the second method of the above linked list inversion problem, which will not be repeated here.

Complete code
typedef struct ListNode Node;
bool isPalindrome(struct ListNode* head){
   Node *fast = head;
   Node *slow = head;
   Node *prev = NULL;
   if(head->next == NULL)
        return true;

   while(fast && fast->next)
   {
       prev = slow;   //  Keep the previous node of slow
       slow = slow->next;   //Come on, take two steps
       fast = fast->next->next; //Come on, take two steps
   }
   prev->next = NULL;//Disconnect from prev node

   //Linked list inversion
   Node *newhead = NULL;
   Node *cur = slow;
   while(cur)
   {
       Node *next =cur->next;

       cur->next = newhead;
       newhead = cur;
       cur = next;
   }

   //Compare the two linked lists
   while(head)
   {
       if(head->val == newhead->val)
       {
            head = head->next;
            newhead = newhead->next;
       }
       else
            return false;    
   }
   return true;
}
e *newhead = NULL;
   Node *cur = slow;
   while(cur)
   {
       Node *next =cur->next;

       cur->next = newhead;
       newhead = cur;
       cur = next;
   }

   //Compare the two linked lists
   while(head)
   {
       if(head->val == newhead->val)
       {
            head = head->next;
            newhead = newhead->next;
       }
       else
            return false;    
   }
   return true;
}

Topics: Algorithm data structure leetcode linked list