【牛客网面试必刷TOP101】链表篇(三)

链表

  • 一、前言
  • 二、学习刷题网站
  • 三、刷题
    • <1>单链表的排序
      • ①模拟数组
      • ②归并排序
    • <2>链表的奇偶重排
      • 双指针
  • 三、小结

一、前言

链表是数据结构中重要的一个章节,他的重要性也不言而喻,在未来不管是笔试还是面试都会遇到这类的题目,所以接下来我就会把一些链表的常考的题目全部整理出来供大家学习指正。


二、学习刷题网站

【牛客网面试必刷TOP101】链表篇(三)_第1张图片

点击下面链接即可进行刷题学习
开始刷题

三、刷题

先说明一下一些题目取自牛客网面试必刷TOP101
里面的一些题目在我以前的文章详细写到过,如果没有用新的方法就不会再做讲解
链表题目(一)
链表题目(二)
环状链表

<1>单链表的排序

题目链接

描述:

给定一个节点数为n的无序单链表,对其按升序排序。
数据范围:0 要求:时间复杂度 O(nlogn)

示例1

输入:{1,3,2,4,5}
返回值:{1,2,3,4,5}

示例2

输入:{-1,0,-2}
返回值:{-2,-1,0}

思路分析:

①模拟数组

把每个节点的地址放到数组中,然后在数组中对他们的值进行排序,最后在重新构建链表。

int cmp(struct ListNode** e1, struct ListNode** e2)
{
    return (*e1)->val - (*e2)->val;
}

struct ListNode* sortInList(struct ListNode* head ) {
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* nums[100000];
    int count = 0;
    struct ListNode* cur = head;
    while(cur)
    {
        nums[count++] = cur;
        cur = cur->next;
    }
    qsort(nums, count, sizeof(struct ListNode*), cmp);
    head = nums[0];
    cur = head;
    for(int i = 1; i < count; i++)
    {
        cur->next = nums[i];
        cur = cur->next;
    }
    cur->next = NULL;
    return head;
}

②归并排序

前面我们做过合并两个有序链表,那我们这个题也可以用这种思路,从中间节点的前一个节点断开,合并两个链表,那么怎么分成两个链表呢,得用到三个指针:

前面我们写过找到中间节点的题目,就是快慢指针,现在我们要找到中间节点的前一个,那么就让slow和fast指针都先走一次,mid就在他们的中间,fast走到尾即可。

奇数情况:
【牛客网面试必刷TOP101】链表篇(三)_第2张图片
偶数情况:
【牛客网面试必刷TOP101】链表篇(三)_第3张图片
但是我们拆分完以后发现两个链表不一定为有序的,这时候就要用到归并思想:

如果我们把链表分成单个的节点,那么每个节点都是有序的,然后就可以用合并两个有序链表的方法,怎么把链表分成单个节点呢?答案是递归,如此一来每次要排序的都是有序链表。

对于递归排序在以前的文章里有过详细讲解:
八大排序,你都掌握了吗?

//合并两个有序链表
struct ListNode* _sortInList(struct ListNode* n1, struct ListNode* n2)
{
    if(n1 == NULL)
    {
        return n2;
    }
    if(n2 == NULL)
    {
        return n1;
    }
    struct ListNode* head;
    //小的值链接到下一个
    if(n1->val < n2->val)
    {
        head = n1;
        head->next= _sortInList(n1->next, n2);
    }
    else
    {
        head = n2;
        head->next= _sortInList(n1, n2->next);
    }
    return head;
}

struct ListNode* sortInList(struct ListNode* head ) {
    if(head == NULL || head->next == NULL)
    {
        return head;
    }
    struct ListNode* slow = head, *mid = head->next, *fast = head->next->next;
    while(fast && fast->next)
    {
        slow = slow->next;
        mid = mid->next;
        fast = fast->next->next;
    }
    slow->next = NULL;
    struct ListNode* node1 = sortInList(head);
    struct ListNode* node2 = sortInList(mid);
    struct ListNode* new = _sortInList(node1, node2);
    return new;
}

<2>链表的奇偶重排

题目链接

描述

给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。
注意是节点的编号而非节点的数值。
数据范围:节点数量满足 0 ≤ n ≤ 10^5,节点中的值都满足0≤val≤1000
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

示例1

输入:{1,2,3,4,5,6}
返回值:{1,3,5,2,4,6}
说明:
1->2->3->4->5->6->NULL
重排后为
1->3->5->2->4->6->NULL

示例2

输入:{1,4,6,3,7}
返回值:{1,6,7,4,3}
说明:
1->4->6->3->7->NULL
重排后为
1->6->7->4->3->NULL
奇数位节点有1,6,7,偶数位节点有4,3。重排后为1,6,7,4,3

备注:
链表长度不大于200000。每个数范围均在int内。

思路分析:

双指针

我们可以利用两个指针,把奇数节点串成一个链表,偶数位串成一个链表,最后再把两个链表相连接。
这里最重要的是条件判断,一定要画图!!!
我们可以只判断偶数节点和偶数节点的下一位不为空,那么一定可以达成条件:
【牛客网面试必刷TOP101】链表篇(三)_第4张图片

这是有奇数个节点的情况,结尾自动链接到NULL,如果是偶数个节点,就是以cur->next == NULL为结束标志,那么他的结尾也是自动为NULL。

struct ListNode* oddEvenList(struct ListNode* head ) {
    //如果链表为空,不用重排
        if(head == NULL)
            return head;
        //cur开头指向第二个节点,可能为空
        struct ListNode* cur = head->next;
        //prev开头指向第一个节点
        struct ListNode* prev = head;
        //指向cur开头
        struct ListNode* evenhead = cur;
        while(cur != NULL && cur->next != NULL){
            //偶数节点链接
            prev->next = cur->next;
            prev = prev->next;
            //奇数节点链接
            cur->next = prev->next;
            cur = cur->next;
        }
        //奇偶链表相连
        prev->next = evenhead;
        return head;
}

三、小结

在做题前一定要先画好图,尽量想好特殊情况,考虑好结束条件。
链表篇就到此结束,接下来博主会更新更多的题目

点击链接一起刷题吧



你可能感兴趣的:(百炼成钢,链表,面试,数据结构)