Singly linked list brushing questions (1-3)

Table of contents

reverse linked list 

remove element

Merge sorted linked list


reverse linked list 

Leek

 We solve this problem by taking the head node and inserting the head in turn . It should be noted that the next node must be saved before plugging .

struct ListNode* reverseList(struct ListNode* head){
    typedef struct ListNode SL;
    SL* cur = head;
    SL* rhead = NULL;//初始指向空
    while(cur)
    {
        SL* next = cur->next;
        cur->next = rhead;
        rhead = cur;
        cur = next;  
    }
    return rhead;
}

remove element

Leek

Idea 1: To delete the element at position pos , that is, the interface we have implemented before, we need to consider header deletion and whether to pass a secondary pointer .

Idea 2: According to the demonstration picture above, do you have a picture of adding data at the end of the sequence table in your mind ? We can also take the data that meets the conditions for tail insertion, ignore the specified element, and keep its next element.

 1. Initialize the pointer

struct ListNode* removeElements(struct ListNode* head, int val){
typedef struct ListNode SLT;
SLT* newhead = NULL,*tail = NULL;//新链表头和尾
SLT* cur = head;//原链表遍历指针

2. Tail plug  

while(cur)
{
    if(cur->val!=val)//不等
    {
    if(tail == NULL)//第一次
    {
        newhead = tail = cur;        
    }
    else
    {
        tail->next = cur;
        tail = tail->next;
    }
    cur =cur->next;
    else //相等
    {
        SLT* Next = cur->next;
        free(cur);
        cur = Next;
    }
}

The overall logic is fine, but it cannot pass the test case

Let's analyze specific problems in detail. Since we don't know what is wrong, we should be more diligent and write a simple single-linked list for debugging. This method can improve our problem-solving ability and improve our code level.

#pragma once
#include<stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct ListNode
{
    int val;
    struct ListNode* next;
}SLT;
SLT* CreateSList(int* a, int n)//用数组构建链表
{
    SLT* phead = nullptr, * ptail = nullptr;
    int x = 0;
    for (int i = 0; i < n; i++)
    {
        //sacnf("%d",&x);
        SLT* newnode = (SLT*)malloc(sizeof(SLT));//初始化(BuyList())
        if (newnode == nullptr)
        {
            perror("malloc fail");
            exit(-1);
        }
        else
        {
            newnode->val = a[i];
            newnode->next = nullptr;
        }
        if (phead == nullptr)//连结
        {
            phead = ptail = newnode;
        }
        else
        {
            ptail->next = newnode;
            ptail = newnode;
        }
    }
    return phead;
}

struct ListNode* removeElements(SLT* head, int val) {
    SLT* newhead = nullptr, * tail = nullptr;//新链表头和尾
    SLT* cur = head;//原链表遍历指针
    while (cur)
    {
        if (cur->val != val)//不等
        {
            if (tail == NULL)//第一次
            {
                newhead = tail = cur;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
            }
            cur = cur->next;
        }
        else //相等
        {
            SLT* Next = cur->next;
            free(cur);
            cur = Next;
        }
    }
    return newhead;
}
        int main()
        {
            int a[] = { 1,2,6,3,4,5,6};
            SLT* plist = CreateSList(a, sizeof(a) / sizeof(int));
            removeElements(plist, 6);
            return 0;
        }

Can you see what is wrong? Yes, we can see that the program normally deletes the data we want, but the last data does not point to empty, but points to the address of 6 to be deleted .

After solving this problem, replace the test case on Likou and debug again, and find that there is still an error:

This time, the arrays are all the same and are all deleted objects , which will cause the tail to point to empty, and a dereferencing problem will occur, so we add judgment, which is perfect.

struct ListNode* removeElements(SLT* head, int val) {
    typedef struct ListNode SLT;//最好不要这样定义
    SLT* newhead = nullptr, * tail = nullptr;//新链表头和尾
    SLT* cur = head;//原链表遍历指针
    while (cur)
    {
        if (cur->val != val)//不等
        {
            if (tail == NULL)//第一次
            {
                newhead = tail = cur;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
            }
            cur = cur->next;
        }
        else //相等
        {
            SLT* Next = cur->next;
            free(cur);
            cur = Next;
        }
    }
    if(tail)
    tail->next = nullptr;
    return newhead;
}

You can implement the single-linked list debugging function I just made by yourself. When you encounter this kind of reminder when doing questions in the future, you can use template debugging to improve efficiency~.

This question can also be done by adding a sentinel node , which has an advantage: when the tail is inserted into the new linked list, there is no need to worry about the linked list being empty and the first tail insertion is judged to be empty.


struct ListNode* removeElements(struct ListNode* head, int val){
typedef struct ListNode SLT;
SLT* guard,*tail;
 guard = tail = (SLT*)malloc(sizeof(SLT));
SLT* cur = head;
while(cur)
{
    if(cur->val!=val)
    {
        tail->next = cur;
        tail = tail->next;
    cur =cur->next;
    }
    else 
    {
        SLT* Next = cur->next;
        free(cur);
        cur = Next;
    }
}
     tail->next = NULL;//避免删除最后一个数据前一个数据指向野指针
     SLT* newhead = guard->next;
     free(guard);
    return newhead;
}

Note that when returning, it is not clearly stated that guard cannot be returned, but its next node, which is the node that stores valid data, is returned. It is valid to return even if a null pointer is passed. Everyone can feel it for themselves.

Merge sorted linked list

Leek

Idea: Take a small tail insert, and when one of them ends, link the remaining data to the new array (orderly).

Here we use sentinel nodes for simplicity. 

The above test case shows that if one of them is empty or both are empty, we can directly return their addresses without comparison , so that it can pass perfectly.

 

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    /*if(list1 == NULL)//非哨兵位
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }*/
    struct ListNode* guard,*tail;
    guard = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
    while(list1 && list2)
    {
        if(list1->val < list2->val)
        {
                tail->next = list1;
                tail = tail->next;
            list1 = list1->next;
        }
        else
        {
                tail->next = list2;
                tail = tail->next;
            list2 = list2->next;
        }
    }
if(list1)//剩余链接
    tail->next = list1;
if(list2)
    tail->next = list2;
    struct ListNode* newhead = guard->next;
    free(guard);
return newhead;
}

Different from the previous question, this question does not need to worry about the wild pointer of the end node. It can be seen that the sentry position is very convenient.

in conclusion:

  • With a sentinel bit, you don't need to pass the secondary pointer (only change the structure)
  • Tail plug is very convenient
  • Singly linked list is not commonly used

Guess you like

Origin blog.csdn.net/dwededewde/article/details/131229601