Bootstrap

玩转链表,从这8道经典链表oj题开始

1.移除链表元素

在这里插入图片描述
大体思路分析:用cur指针去遍历链表,用prev指针记录cur的前一个节点,如果cur->val != val,则prev=cur,cur=cur->next;如果cur->val == val,则用next指针记录cur指针的下一个节点,free(cur),prev=next,cur=next,prev=cur;继续如此循环。
但是如果第一个链表的数据就是val就又是一种情况。如果连续几个都是val又是什么情况呢?
下面我们画图来分析。

  1. 常规情况: 在这里插入图片描述
  2. 连续几个val: 在这里插入图片描述
    我们发现常规情况也能处理连续几个val的情况。
  3. 第一个就是val,意味着要改变头指针 在这里插入图片描述
    特别注意这种情况:prev==NULL;此时free掉cur,让head指向next,next再给个cur;
    在这里插入图片描述

代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur = head;
    struct ListNode* prev =NULL;
    while(cur)
    {
        if(cur->val != val)
        {
            prev = cur;
            cur = cur->next;
        }
        else
        {
            struct ListNode* next = cur->next;
            if(prev == NULL)
            {
                free(cur);
                head = next;
                cur = next;
            }
            else
            {
                free(cur);
                prev->next = next;
                cur = next;
            }
        }
    }
    return head;
}

2. 反转链表

在这里插入图片描述

2.1 三指针翻转法

令指针n1等于NULL,n2等于head,n3记录n2的下一个,让n1,n2去倒指针的方向,n3记录下一个节点。
在这里插入图片描述
我们发现当n2指针等于NULL时,链表全部反转,所以n2 == NULL为结束条件。
特殊情况:
在这里插入图片描述
此时n3已经为NULL了,如果继续n3=n3->next,就出现了访问空指针的情况。所以这种情况也要判断一下。另外,如果头指针为NULL,直接 return NULL
代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


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

2.2 头插法

cur取原链表节点,头插到newHead所在的链表,用next指针记录cur的下一个,同时更新newHead
在这里插入图片描述
我们发现当cur走到NULL,遍历结束,链表完成反转。所以cur==NULL为循环结束条件。
代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* newHead = NULL;
    struct ListNode* cur = head;
    while(cur)
    {
        struct ListNode* next = cur->next;
        cur->next = newHead;
        newHead = cur;
        cur = next;
    }
    return newHead;
}

3. 链表的中间节点

在这里插入图片描述

  1. 奇数个数据:
    在这里插入图片描述
  2. 偶数个数据
    在这里插入图片描述
    我们发现结束条件为fast为空或fast->next为空。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* slow, *fast;
    slow = fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

4. 链表的倒数第k个节点

在这里插入图片描述
fast先走k步,然后fastslow同时走,当fast走到空,slow就是倒数第k个节点。

在这里插入图片描述
代码:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    struct ListNode* slow, *fast;
    slow = fast = pListHead;
    while(k--)
    {
        if(fast == NULL)
            return NULL;
        fast = fast->next;
    }
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

5.合并两个有序链表

在这里插入图片描述
每次取小的节点尾插到新链表。如果list1先走完,把剩下的list2直接尾插给tail,反之,剩下的list1尾插给tail
在这里插入图片描述
特殊情况处理:当list1为空,返回list2,当list2为空,返回list1

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }
    struct ListNode* head=NULL, *tail=NULL;
    while(list1 && list2)
    {
        if(list1->val < list2->val)
        {
            if(tail == NULL)
            {
                head = tail = list1;
            }
            else
            {
                tail->next = list1;
                tail = list1; 
            }
            list1 = list1->next;
        }
        else
        {
            if(tail == NULL)
            {
                head = tail = list2;
            }
            else
            {
                tail->next = list2;
                tail = list2;
            }
            list2 = list2->next;
        }
    }
    if(list1)
    {
        tail->next = list1;
    }
    if(list2)
    {
        tail->next = list2;
    }
    return head;
}

6. 链表分割

在这里插入图片描述

遍历原链表,把小于x的插入到一个链表1,把大于等于x的插入到一个链表2,链表1和链表2连接起来。
在这里插入图片描述
我们再考虑一下其他情况:
在这里插入图片描述
在这里插入图片描述
这种情况链表就成环了,此时程序死循环。只要把greattail->next== NULL就行了;
代码:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
         struct ListNode*head1,*head2,*tail1,*tail2;
         head1=tail1=(struct ListNode*)malloc(sizeof(struct ListNode));
         head2=tail2=(struct ListNode*)malloc(sizeof(struct ListNode));
        tail1->next=NULL;
        tail2->next=NULL;
        struct ListNode* cur=pHead;
        while(cur)
        {
            if(cur->val<x)
            {
                tail1->next=cur;
                tail1=tail1->next;
            }else
            {
                tail2->next=cur;
                tail2=tail2->next;
            }
            cur=cur->next;
        }
    tail1->next=head2->next;
        tail2->next=NULL;//**必须要有,不然会在某些极端测试用例下会形成一个环,
                          //那便 是死循环了,栈溢出。**
        pHead=head1->next;
        free(head1);
        free(head2);
         return pHead;
    }
};

7.链表的回文结构

在这里插入图片描述
思路:先找到链表的中间节点,然后从中间节点开始把链表逆置,rHead,A同时走并比较。如果有不相等的,就说明不是回文,return false;如果有一个走完,return ture;
在这里插入图片描述
我们发现,偶数和奇数,这种方法都适用,因为即使把链表后半段逆置了,2也是指向3的。
代码:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/



class PalindromeList {
public:
struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* slow, *fast;
    slow = fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}
    
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* newHead = NULL;
    struct ListNode* cur = head;
    while(cur)
    {
        struct ListNode* next = cur->next;
        cur->next = newHead;
        newHead = cur;
        cur = next;
    }
    return newHead;
}
    bool chkPalindrome(ListNode* A)
    {
         struct ListNode *mid= middleNode(A); 
        struct ListNode *rHead=reverseList(mid); 
        while(A&&rHead) 
        { 
            if(A->val!=rHead->val) 
            { 
                return false; 
            } 
            else 
            { 
                A=A->next; 
                rHead=rHead->next; 
            } 
        } 
        return true; 
    }
};

8.相交链表

在这里插入图片描述
思路:
在这里插入图片描述
代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    struct ListNode* tailA = headA, *tailB = headB;
    int lenA = 1, lenB =1;
    while(tailA->next)
    {
        tailA = tailA->next;
        ++lenA;
    }
    while(tailB->next)
    {
        tailB = tailB->next;
        ++lenB;
    }
    if(tailA!=tailB)
    {
        return NULL;
    }
    struct ListNode* shortList = headA, *longList = headB;
    if(lenA > lenB)
    {
        longList = headA;
        shortList = headB;
    }
    int gap = abs(lenA-lenB);
    while(gap--)
    {
        longList = longList->next;
    }
    while(shortList && longList)
    {
        if(shortList == longList)
        {
            return shortList;
        }
        shortList = shortList->next;
        longList = longList->next;
    }
    return NULL;
}
;