链表---OJ题


练习题1

移除链表元素

在这里插入图片描述
在这里插入图片描述
解决方案:
1.创建一个新的头节点,不相等尾插即可。
2.使用cur和prev遍历数组,遇到相等的删除即可。

//定义一个新链表 不是val尾插
 struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* cur = head;
    struct ListNode* tail = guard;
    
     while(cur)
     {
        if(cur->val != val)
        {
            tail->next = cur;
            tail = tail->next;

            cur = cur->next;
        }
        else
        {
            struct ListNode* del = cur;
            cur = cur->next;
            free(del);
        }  
     }
    if(tail != NULL)
    {
        tail->next = NULL;
    }
    head = guard->next;
    free(guard);
    return head;
 }

//遍历删除
struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* cur = head;
    struct ListNode* prev = head;
    while(cur)
    {
        if(cur->val == val)
        {
            //1、头删
            if(cur == head)
            {
                head = head->next;
                free(cur);
                cur = head;
            }
            //2、非头删
            else
            {
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}

练习题2

反转链表

在这里插入图片描述在这里插入图片描述

解决方案:
1.尾插变头插。
2.改变指针的指向。

//1.尾插变头插
struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL;
    while(cur)
    {
        struct ListNode* next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    return newhead;
}

//2.改变指针的指向
struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur = head;
    //struct ListNode* next = NULL;
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    guard->next = NULL;

    while(cur)
    {
        struct ListNode* next = cur->next;
        cur->next = guard->next;
        guard->next = cur;
        cur = next;
    }
    head = guard->next;
    free(guard);
    return head;
}

练习题3

链表的中间节点

在这里插入图片描述
在这里插入图片描述
解决方案:
使用快慢指针,快指针每次+2,慢指针每次+1。结束条件为fast且fast->next不为空,如果没有fast->next这个条件,会出现问题,因为一次需要+2。画图说明原因:
在这里插入图片描述

//只能遍历一遍 快慢指针 快+2 慢+1 

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow = head;
    struct ListNode* fast = head;

    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

练习题4

链表中倒数第k个节点

在这里插入图片描述
解决方案:
使用快慢指针解决问题,首先让快指针向后移动k步,此时fast和slow相差k步(这个过程要注意边界问题),然后让fast继续向后直到为空为止,每一次slow++,那么就找到了倒数第K个节点的位置,就是slow。

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // write code here
    if(pListHead == NULL)
    {
        return NULL;
    }
    
    struct ListNode* fast = pListHead;
    struct ListNode* slow = pListHead;
    
    while(k--)
    {
        fast = fast->next;
        if(fast == NULL)
            break;
    }
    
    if(k < 1)
    {
         while(fast)
        {
            fast=fast->next;
            slow=slow->next;
        }
    }
    else
    {
        slow = NULL;
    }
    return slow;
}

练习题5

合并两个有序链表

在这里插入图片描述
在这里插入图片描述
解决方案:
新建一个有哨兵位的头节点,然后通过三个指针做这个题,两个指针用来遍历给定的两个升序链表,另一个用来做新开辟的节点的指针,每次插入+1。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* cur1 = list1;
    struct ListNode* cur2 = list2;
    struct ListNode* cur = guard;
    cur->next = NULL;
    
    while(cur1 && cur2)
    {
        if(cur1->val <= cur2->val)
        {
            cur->next = cur1;
            cur = cur->next;
            cur1 = cur1->next;
        }
        else
        {
            cur->next = cur2;
            cur = cur->next;
            cur2 = cur2->next;
        }
    }
    
    if(cur1)
    {
        cur->next = cur1;
    }
    if(cur2)
    {
        cur->next = cur2;
    }
    
    struct ListNode* newhead = guard->next;
    free(guard);
    return newhead;
}

练习题6

链表分割

在这里插入图片描述

解决方案:
新开辟两个带哨兵位的头节点,第一个存储大于等于x的节点,第二个存储小于x节点。将小于x的最后一个节点的next指向第一个哨兵位的next地方,最后一定要将大于等于x的最后一个节点的next置空,否则会造成回环。

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode* lessGuard = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* greaterGuard = (struct ListNode*)malloc(sizeof(struct ListNode));
        lessGuard->next = NULL;
        greaterGuard->next =NULL;
        struct ListNode* lesstail = lessGuard;
        struct ListNode* greatertail = greaterGuard;
        struct ListNode* cur = pHead;
        
        while(cur)
        {
            if(cur->val < x)
            {
                lesstail->next = cur;
                lesstail = lesstail->next;
            }
            else
            {
                greatertail->next = cur;
                greatertail = greatertail->next;
            }
            
            cur = cur->next;
        }
        
        lesstail->next = greaterGuard->next;
        
        //尾节点的数据置NULL 否则会形成回环
        greatertail->next = NULL;
        
        struct ListNode* head = lessGuard->next;
        free(lessGuard);
        free(greaterGuard);
        return head;
    }
};

练习题7

链表的回文结构

在这里插入图片描述解决方案:
1.找到中间的节点
2.从中间的节点开始向后开始逆置 3.从head和rmid开始遍历至NULL结束,若一直相等,那么证明是回文结构。

class PalindromeList {
public:
    struct ListNode* middleNode(struct ListNode* head){
        struct ListNode* slow = head;
        struct ListNode* fast = head;

        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow=slow->next;
        }
        return slow;
    }
    
    struct ListNode* reverseList(struct ListNode* head){
        struct ListNode* prev = NULL;
        struct ListNode* cur = head;

         while(cur)
         {
             struct ListNode* next = cur->next;
             cur->next = prev;

             prev = cur;
             cur = next;
         }
         return prev;
    }
    bool chkPalindrome(ListNode* head) {
        // write code here
        struct ListNode* mid = middleNode(head);
        struct ListNode* rmid = reverseList(mid);
        
        while(head && rmid)
        {
            if(head->val != rmid->val)
            {
                return false;
            }
            head = head->next;
            rmid = rmid->next;
        }
        return true;  
    }
};

练习题8

相交链表

在这里插入图片描述解决方案:
1.判断两个链表的尾节点是否相同,如果相同证明是相交的,否则不是相交的。
2.计算两个链表的长度
3.让长链表遍历abs(lenB - lenA)
4.长短链表分别开始遍历,遍历至相同的时候,说明招到了相交的节点。

//尾节点的地址是否相同
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* curA = headA, *curB = headB;
    //判断是否为空链表
    if(curA == NULL || curB == NULL)
    {
        return NULL;
    }

    //判断长度
    int lenA = 1;
    while(curA->next)
    {
        curA = curA->next;
        ++lenA;
    }

    int lenB = 1;
    while(curB->next)
    {
        curB = curB->next;
        ++lenB;
    }

    if(curB != curA)
    {
        return NULL;
    }

    struct ListNode* longList = headA, *shortList = headB;
    if(lenA < lenB)
    {
        longList = headB;
        shortList = headA;
    }
    int gap = abs(lenB - lenA);

    while(gap--)
    {
        longList = longList->next;
    }

    while(longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
    return shortList;
}

练习题9

判断是否有回环

在这里插入图片描述
在这里插入图片描述
解决方案:
1.定义两个指针,快慢指针解决这个问题。
2.快指针每次+2,慢指针每次+1。
3.直到快指针 = 满指针 证明有回环。

为什么快指针+2 慢指针+1就一定可以找到呢?
因为每次相差1,当慢指针进入回环的时候,假设和快指针相差N,那么每走一步,距离都会缩小1,N N-1 N-2 ------ 1 ------0 , 因此这个方向是可以使用的。

如果快指针一次+3 慢指针+1可以找到吗?
因为每次相差2,当慢指针进入回环的时候,假设和快指针相差N,那么每走一步,距离都会缩小2,如果N为偶数,那么可以找到。如果为奇数,会出现1 -----> - 1的情况,当距离为-1时,代表着慢指针会超过快指针,若圆环内的元素为C ,则新的距离差为C-1,C-1为偶数则有机会相等,C-1为奇数的话一定相等。

//快慢指针解决问题
bool hasCycle(struct ListNode *head) {
    struct ListNode* fast, *slow;
    fast = slow =head;

    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

        if(slow == fast)
            return true;
    }
    return false;
}

练习题10

找到环行链表的入口

在这里插入图片描述
在这里插入图片描述

解决方案1:
这个问题类似与找两个链表的相交节点
1.判断是否有回环
2.将相遇的前一个节点的next置NULL
3.一个指针从head开始,另一个从相遇点开始
4.分别计算lenA , lenB 长链表先遍历abs(lenB-lenA)
5.长短链表依次向后遍历,相等即是交点,即环形链表的入口

解决方案2:
在这里插入图片描述

Solution1

struct ListNode *detectCycle(struct ListNode *head) {
    int is_circle = 0;//有无环的标志
    struct ListNode* fast, *slow, *prev_fast;
    fast = slow = head;
    prev_fast = NULL;
    if(fast == NULL)
    {
        return NULL;
    }

    while(fast && fast->next)
    {
        prev_fast = fast->next;
        fast = fast->next->next;
        slow = slow -> next;

        if(slow == fast)
        {
            is_circle = 1;//有环
            prev_fast->next = NULL;//交点的前一个next置NULL
            break;
        }
    }
    
    if(is_circle == 0)
    {
        return NULL;
    }

    else
    {
        struct ListNode* curA = head;
        struct ListNode* curB = slow;

        if(head==NULL || slow==NULL)
        {
            return NULL;
        }

        int lenA = 1;
        while(curA->next)
        {
            curA = curA->next;
            lenA += 1;
        }

        int lenB = 1;
        while(curB->next)
        {
            curB = curB->next;
            lenB += 1;
        }
        
        struct ListNode* LongList = head;
        struct ListNode* ShortList = slow;
        if(lenA < lenB)
        {
            LongList = slow;
            ShortList = head;
        }
        int gap = abs(lenB - lenA);
        while(gap--)
        {
            LongList = LongList->next;
        }
        
        while(LongList != ShortList)
        {
            LongList = LongList->next;
            ShortList = ShortList->next;
        }
        return LongList;
    }
}

Solution2

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

        if(slow == fast)
        {
            struct ListNode* meet = slow;
            while(meet != head)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }

    return NULL;
}

练习题11

复制带随机指针的链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

struct Node* copyRandomList(struct Node* head) {
    //1.插入copy节点
    struct Node* cur = head;
    struct Node* copy = NULL;
    struct Node* next = NULL;
    while(cur)
    {
        //赋值链接
        next = cur->next;
        copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        cur->next = copy;
        copy->next = next;

        //迭代向后走 
        cur = next;
    }

    //2.更新copy的random
    cur = head;
    while(cur && cur->next)
    {
        copy = cur->next;
        
        if(cur->random == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;
        }

        //迭代
        cur = copy->next; 
    }

    //3.copy节点剪下来链接到一起,恢复原链表
    cur = head;
    struct Node* compHead = NULL, *copyTail = NULL;
    while(cur && cur->next)
    {
        copy = cur->next;
        next = copy->next;

        //取节点尾插
        if(copyTail == NULL)
        {
            compHead = copyTail = copy;
        }
        else
        {
            copyTail->next = copy;
            copyTail = copyTail->next;
        }
        
        //恢复原链表链接
        cur->next = next;

        //迭代
        cur = copy->next;
    }

    //尾节点的next置NULL
    if(cur)
        cur->next = NULL;
    
    return compHead;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值