Analysis of related OJ questions about linked lists

Table of contents

⭐1. Remove linked list elements 

⭐2. Reverse linked list

⭐3. Find the middle node of the linked list

⭐4. Find the k-th node from the bottom of the linked list

⭐ 5. Merge two ordered linked lists

⭐6. Palindrome structure of linked list

⭐ 7. Intersecting linked list

⭐8. Ring linked list

⭐9. The first node of the linked list entering the ring



⭐1. Remove linked list elements 

Links:  remove linked list elements

Idea 1: Back and forth pointer method

1. Define two pointers cur, prev

Cur is used to traverse, prev is used to point to the previous node of cur, which is convenient for connecting the linked list when deleting

2. Traverse the linked list directly, if the val value of the node is equal to the val value to be deleted, then free the node

Time complexity O(n), space complexity O(1)

 ⭐Code implementation:

struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode*cur=head,*prev=NULL;
while(cur)
{if(cur->val!=val)
{
 prev=cur;//让prev指向cur节点的前一个节点
 cur=cur->next;
}
else 
{
    if(prev==NULL)//判断删除的是否为头节点
    {
        head=cur->next;//为头节点的话,指向头节点的指针后移
        free(cur);
        cur=head;
    }
    else 
    {
        prev->next=cur->next;//删除节点的前一个节点连上删除节点的后一个节点
        free(cur);
        cur=prev->next;//更新一下cur指向的节点
    }
}


}
return head;
}

Note: If the deleted node is the head node, you need to update the head node.

Idea 2:

Insert the tail of the node not equal to val into a new linked list, and delete the node equal to val directly (free)

Time complexity O(n), space complexity O(1)

 

 ⭐Code implementation:

struct ListNode*  removeElements(struct ListNode* head, int val) {
    //创建一个什么都不存的头节点(哨兵位节点)
    struct ListNode* newhead = (struct ListNode*) malloc(sizeof(struct ListNode));
    struct ListNode*tail=newhead,*cur = head;
   //tail指向新链表的尾,cur用来遍历新链表
        while(cur!=NULL)
        {
            if(cur->val == val)
            {
                struct ListNode *tmp = cur->next;
                   free(cur);
                   cur=tmp;
            }
            else
            {
                tail->next=cur;
                tail=cur;
                cur=cur->next;
            }
        }

        tail->next=NULL;//注意要把新链表的尾置空
      head=newhead->next;//把新链表赋给head
      free(newhead);
      return head;

    }

1. This method does not need to judge whether the deleted node is the head node,

But it should be noted that

2. You must assign the next node of the head node of the new linked list to head instead of assigning the head node of the new linked list to head

3. If the last node is a deleted node, then the tail node of the new linked list needs to be empty, otherwise the next of the tail node of the new linked list will become a wild pointer

⭐2. Reverse linked list

Links:  reverse linked list

Ideas :

Remove the nodes in the original linked list one by one and insert them into the new linked list

Illustration:

⭐ Code implementation:

struct ListNode* reverseList(struct ListNode* head){

 struct ListNode*newhead=NULL;//新链表的头节点
 struct ListNode*cur=head,*tmp=NULL;
 while(cur)
 {
     tmp=cur;
     cur=cur->next;
     tmp->next=newhead;//头插
     newhead=tmp;
 }   
 return newhead;
}

 Time complexity O(n), space complexity O(1)

⭐3. Find the middle node of the linked list

Link: the middle node of the linked list

 Idea: fast and slow pointer method

Set a fast pointer fast and a slow pointer slow to point to the head node at the same time, let the fast pointer go two nodes at a time, and the slow pointer go one node at a time, if the number of linked list nodes is odd, then fast->next is When it is empty, slow is the intermediate node. If the linked list nodes are even, then when fast is empty, slow is the intermediate node. After the loop ends, return to slow

The number of linked list nodes is odd:

 The number of linked list nodes is even:

⭐ Code implementation:

struct ListNode* middleNode(struct ListNode* head){

struct ListNode*fast=head,*slow=head;
while(fast&&fast->next)
{
    fast=fast->next->next;//一次走两步
    slow=slow->next;//一次走一步
}

return slow;

}

⭐4. Find the k-th node from the bottom of the linked list

Link: Find the kth node from the bottom of the linked list

 Idea: fast and slow pointer method

First set two pointers fast and slow, first let fast take k steps, and judge that if the value of k is too large, when fast is already empty, it means that the value of k is illegal, and returns empty. If the value of K is legal, then let fast and slow go together. When fast is empty, the position of slow is the Kth node from the bottom

Illustration:

⭐ Code implementation:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
   struct ListNode*fast=pListHead, *slow=pListHead;
     if(!pListHead||k<=0)return NULL;
   while(k--)
   {
    if(fast)
     { 
        fast=fast->next;//fast不为空就走
     }
   else
    {
       return NULL;//fast为空说明不合法,返回NULL
    }
   }
   while(fast)
   {
    fast=fast->next;//fast,slow一起走
    slow=slow->next;
   }
   
   return slow;
}

It should be noted here that if k<=0, it means that it is illegal, and it is also illegal when K is too large.

⭐ 5. Merge two ordered linked lists

Link: Merge two sorted linked lists

Idea: sentry position solution

Create a new node (a sentinel node, a node that does not store a value) as a new linked list, compare the val values ​​in the two original linked lists, take the smaller tail and insert it into the new linked list, eliminating the need to judge whether the two linked lists are empty situation, but the space needs to be freed before returning, time complexity O(n), space complexity O(1)

⭐ Code implementation:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
struct ListNode*phead=NULL,*tail=NULL;
struct ListNode*tmp=NULL;
phead=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
if(list1==NULL)
{
    return list2;
}
if(list2==NULL)
{
    return list1;
}
while(list1&&list2)
{
    if(list1->val<=list2->val)
    {
        tail->next=list1;
        list1=list1->next;
        tail=tail->next;
        tail->next=NULL;
    }
    else{
        tail->next=list2;
        list2=list2->next;
        tail=tail->next;
        tail->next=NULL;
    }
}
if(list1==NULL)
{
      tail->next=list2;
}
else{
    tail->next=list1;
}
  struct ListNode* head=phead->next;
   free(phead);//释放申请的空间,防止内存泄漏
return head;
}

⭐6. Palindrome structure of linked list

Link: palindrome structure of linked list

Ideas:

First find the middle node of the linked list, then start from the middle node, insert the following nodes into a new linked list (reverse linked list), and then compare the two linked lists, if they are equal, then it is a palindrome structure, If one node is not equal then it is not a palindrome

We can easily solve this problem by finding the middle node of the linked list and reversing the linked list above.

⭐Code implementation:

class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        // write code here
        ListNode*fast=A;
        ListNode*slow=A;
        ListNode*phead=NULL;
        while(fast&&fast->next)//为了求出中间节点
        {
            fast=fast->next->next;
            slow=slow->next;
        }
        while(slow)//将链表反转
        {
            ListNode*tmp=slow;
            slow=slow->next;
            tmp->next=phead;
            phead=tmp;
        }
        while(phead)
        {
            if(phead->val!=A->val)
            {
                return false;//不相等就返回false
            }
            else {
            phead=phead->next;
            A=A->next;
            }
        }
        return true;
    }
};

⭐ 7. Intersecting linked list

Links: Intersecting linked lists

Ideas:

First traverse the two linked lists, find their lengths while traversing, and end the traversal at their last node (in order to compare whether they intersect, because if they intersect, the last node must be the same), then compare them at the end Whether a node is equal, if not equal, return empty, if equal, then compare their lengths, let the long linked list take their length difference first, and then walk together, the first node they meet is the first one they intersect node

⭐Code implementation:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode*pre=headA,*cur=headB;
    struct ListNode*longlist=headA,*shortlist=headB;
    int cnt1=1,cnt2=1;
    while(pre->next)//遍历链表
    {
        cnt1++;//求长度
        pre=pre->next;
    }
    while(cur->next)//遍历链表
    {
        cnt2++;//求长度
        cur=cur->next;
    }
    if(pre!=cur)//比较最后一个节点
    {
         return NULL;//不相等返回空
    }
    if(cnt1<cnt2)
    {
       longlist=headB;//为了让长的先走
       shortlist=headA;
    }
    int a=abs(cnt1-cnt2);//算长度差
    while(a--)
    {
      longlist=longlist->next;//让长的先走它们的长度差步
    }
    while(longlist!=shortlist)//相遇的节点就为第一个相交的节点
    {
       longlist=longlist->next;
       shortlist=shortlist->next;
    }
    return longlist;


}

⭐8. Ring linked list

Links: circular linked list

Idea: fast and slow pointer method

Let the fast and slow pointers walk at the same time, but the fast walks two steps at a time, and the slow pointer walks one step at a time. If the linked list does not have a ring, then the fast and slow will not meet. When the fast is empty, it will return false. They will meet.

⭐Code implementation:

bool hasCycle(struct ListNode *head) {
    struct ListNode*fast=head,*slow=head;
    while(fast&&fast->next)
    {
      
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow)
        {
            return true;
        }
    }
    return false;
    
}

Why does the fast pointer take two steps at a time, and the slow pointer takes one step so that they can meet?

Reason:
Assuming a linked watch with a ring, both pointers will enter the ring at the end, the fast pointer advances to the ring, and the slow pointer enters the ring backward.

Best case: when the slow pointer just enters the ring, it may meet the fast pointer,

Worst case: the distance between the two pointers is exactly the length of the ring. At this time, every time the two pointers move
, the distance between them will be reduced by one step, and there will be no situation where it happens to be a loop every time. Therefore: before the slow pointer walks a circle, the
fast pointer must be able to catch up with the slow pointer Yes, now they meet.

Let us assume that when the slow pointer enters the ring, the distance between the fast pointer and the slow pointer is N, the fast pointer takes two steps at a time, and the slow pointer takes one step at a time, and their distance is reduced by 1 for each step, from N To N-1, to N-2...3, 2, 1, and finally to 0, at this time the two meet


So can the fast pointer go multiple steps at a time (three steps or more)?
The fast pointer walks 3 steps at a time, walks 4 steps, ... n walks?
This situation is sometimes possible, such as some special cases.

When the slow pointer enters the ring, the distance between them is N, and then to N-2, N-4..., if N is an odd number, then N-2*n (n is the number of steps) will never be equal to 0, after all, the pointer cannot take half a step ; and if N is an even number, then N-2 *n will always be equal to 0. From this, we can know that the fast pointer moves 3 steps at a time, and the fast and slow pointers may meet, or they may not. It depends on when they meet.

⭐9. The first node of the linked list entering the ring

Links: circular linked list||

Ideas:

First use the method of the previous question: let the fast and slow pointers move at the same time, but the fast moves two steps at a time, and the slow pointer moves one step at a time. If the linked list does not have a ring, then the fast and slow will not meet, and the fast will go to empty time Just return false, if there are loops then they will meet.

Then save the point where they meet, and then let the slow pointer start from the starting point, and the fast pointer start from the meeting point. They all walk one step at a time. At this time, the point where they meet is the first node of the linked list entering the ring.

⭐Code implementation:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode*fast=head,*slow=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            struct ListNode *l1=fast;
            struct ListNode *l2=head;
            while(l1!=l2)
            {
                l1=l1->next;
                l2=l2->next;
            }
            return l1;
        }
    }
    return NULL;
    
}

Reasons why they meet at the circular entrance:

H is the starting point of the linked list, E is the entry point of the ring, and the point where M meets the ring is set: the
length of the ring is R, the distance from H to E is LE, and the distance from M to X is then: the distance from M to E is RX
When judging the ring, the length of the path taken when the fast and slow pointers meet:
fast: L +X+nR slow: L+x
Note:
1. When the slow pointer enters the ring, the fast pointer may have circled n circles in the ring , n is at least 1
because: the fast pointer advances to the position of M, and finally meets the slow pointer at the position of M.
2. After the slow pointer enters the ring, the fast pointer will definitely catch up with the slow pointer within a circle of the slow pointer The pointer
is because: after the slow pointer enters the ring, the distance between the fast and slow pointer is at most the length of the ring, and when the two pointers are moving, the distance between them is reduced by one step each time, so the fast pointer must be before the slow pointer moves a circle. Can catch up with the slow pointer
and the speed of the fast pointer is twice that of the slow pointer, so the following relationship is: 2*(L+X)=L+X+nR 
The above formula can be obtained: L=nR-X (n is 1 , 2, 3, 4..., the size of n depends on the size of the ring, the smaller the ring, the bigger n)

In extreme cases, assuming n=1, at this time: L=RX

So the fast pointer originally wanted to walk n circles, but at this time the fast pointer started to walk from the meeting point, so it took X steps less, and finally their meeting point was at the entry point of the ring, that is: a pointer starts from the starting position of the linked
list Go, a pointer circles from the meeting point, one
step at a time, and the two pointers will eventually meet at the entry point

Some related OJ questions of the linked list are shared here, 886!

Guess you like

Origin blog.csdn.net/m0_72532428/article/details/130667890