牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素

⚓️作者简介:即将大四的北京某能源高校非科班学生。

座右铭:“九层之台,起于垒土” 。所以学习技术须脚踏实地。

这里推荐一款刷题、模拟面试神器,可助你斩获大厂offer:点我免费刷题、模拟面试

文章目录

  • 前言
  • 面试必刷题
    • 1.链表中倒数最后k个结点
    • 2.删除链表的倒数第n个节点
    • 3. 删除有序链表中重复的元素-I
    • 4.删除有序链表中重复的元素-II

前言

牛客网是一个集笔面试系统、题库、课程教育、社群交流、招聘内推于一体的招聘类网站,更是一个专注于程序员的学习和成长的平台。

在某次浏览博客的过程中,我偶然点进一个链接,注册了牛客账号。一来到牛客首页,我就被其丰富的功能与良好的社区环境所吸引:
在这里插入图片描述
进入题库,更是有最新校招试题与专项练习:在这里插入图片描述
在线编程更是有在线调试功能,可大大提高debug效率:
在这里插入图片描述
问答题下还有超多牛客用户交流:
在这里插入图片描述
总之,牛客是一个专注于程序员的学习和成长的平台,所以我极力推荐大家注册牛客,坚持刷题,充实自己,大厂offer便指日可待。

面试必刷题

 链表,别名链式存储结构或单链表,用于存储逻辑关系为 “一对一” 的数据。与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。

下面来刷几个链表题练练手

1.链表中倒数最后k个结点

题目:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第1张图片

示例:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第2张图片

解题思路

本题为简单题,思路比较清晰:

方法一:
第一次遍历链表,得到链表长度 len,第二次循环,往后走 len - k 步,即可得到倒数第 k 个结点。

C++解题代码:

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pHead, int k) {
        ListNode *t = pHead;
        int len = 0;
        while(t){t = t->next; ++len;}
        t = pHead;
        k = len - k;
        while(k && t){t = t->next;--k;}
        return t;
    }
};

方法二:
维护一个栈或队列,没经过一个元素就压栈或进队,到达尾节点后再将后 k 个元素出栈或将 len - k 个元素出栈,即可得到倒数第 k 个元素。

C++解题代码:
以栈为例:

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pHead, int k) {
        stack<ListNode*> s;
        ListNode* t = pHead;
        while(t) {
            s.push(t); 
            t = t->next;
        }
        while(k - 1 && !s.empty()) {
            s.pop(); 
            --k;
        }
        return s.empty()?nullptr:s.top();
    }
};

方法三:
使用双指针,前面指针先走 k 步,然后后面指针从头开始走,前面指针接着走,前面指针到达尾节点后,后面指针即指向了倒数第 k 个结点。
当前面指针走了不到 k 步就到达尾节点后,说明链表长度小于 k,此时无解。

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pHead, int k) {
        ListNode *first = pHead, *last = pHead;
        while(k && first){
            first = first->next;
            --k;
        }
        if(!first && k != 0) return nullptr;
        while(first){
            first = first->next;
            last = last->next;
        }
        return last;
    }
};

方法一需要扫描链表 2length - k 次,时间复杂度为 O(n),空间复杂度为 O(1)。方法二的时间复杂度为 O(n),空间复杂度为 O(n)。方法三需要扫描链表 length + k 次,时间复杂度为 O(n),空间复杂度为 O(1)。综上,方法一和方法三的性能较好。

2.删除链表的倒数第n个节点

题目:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第3张图片

示例:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第4张图片

解题思路
利用上题的方法三,维护两个指针,不过这次后面指针是指向要删除结点的前一个结点。此外还要添加一个头结点,方便操作。

C++解题代码:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *prehead = new ListNode(0);
        prehead->next = head;
        ListNode *last = prehead, *first = head;
        while(n){ first = first->next; --n; }
        while(first){ first = first->next; last = last->next; }
        ListNode *rednode = last->next;
        last->next = rednode->next;
        delete(rednode);
        
        head = prehead->next;
        delete(prehead);
        return head;
    }
};

本题也属于简单题,理解上一题的方法三这题就不难做出来,查看更多题解,快来这里吧。

3. 删除有序链表中重复的元素-I

题目:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第5张图片
示例:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第6张图片
解题思路:
维护两个指针。
前面指针 first 指向的结点与后面指针 last 指向的结点具有相同的值,则删除当前结点,first 继续向前走,不同则停下;last 指向 first 指向的结点,first 向前走一步。
继续上面步骤。

C++解题代码

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode *first = head, *last;
        while(first){
            last = first;
            first = first->next;
            while(first && first->val == last->val){
                first = first->next;
                free(last->next);
                last->next = first;
            }
        }
        return head;
    }
};

4.删除有序链表中重复的元素-II

题目:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第7张图片

示例:
牛客网面试必刷TOP101之——寻找链表中的倒数结点与删除链表元素_第8张图片

解题思路:

  • step 1:给链表前加上表头,方便可能的话删除第一个节点。
ListNode res = new ListNode(0);
//在链表前加一个表头
res.next = head;
  • step 2:遍历链表,每次比较相邻两个节点,如果遇到了两个相邻节点相同,则新开内循环将这一段所有的相同都遍历过去。
  • step 3:在step 2中这一连串相同的节点前的节点直接连上后续第一个不相同值的节点。
  • step 4:返回时去掉添加的表头。

C++解题代码

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head) return head;
        ListNode *prehead = new ListNode(0);
        prehead->next = head;
        ListNode *cur = prehead;
        while(cur->next && cur->next->next){
            if(cur->next->val == cur->next->next->val){
                int val = cur->next->val;
                while(cur->next && cur->next->val == val)
                    cur->next = cur->next->next;
            }
            else
                cur = cur->next;
        }
        return prehead->next;
    }
};

我希望通过写博客来结束浑浑噩噩的生活,我更希望通过刷题结束人云亦云的思考。刷题不仅仅是刷题,还是我们与自己内心深处的对话。希望我们可以一起在牛客刷题交流,一起收割大厂offer!

你可能感兴趣的:(牛客C++刷题,链表,面试,数据结构)