24届秋招专场·双指针巧解链表套路题

news2024/11/14 14:26:59

在这里插入图片描述

你好,我是安然无虞。

文章目录

    • 合并两个有序链表
    • 分隔链表
    • 合并K个有序链表
    • 链表中倒数最后K个节点
    • 变形题: 删除链表的倒数第N个节点
    • 链表的中点
    • 判断链表是否有环
    • 环形链表II
    • 相交链表

大家好, 好久不见了, 从今天开始, 后面会经常更新笔试面试真题, 准备今年24届秋招的小伙伴可以提前看过来了哦.
在这里插入图片描述
咱们先从链表入手, 基础知识默认大家都会咯, 直接热题开刷, 走着…

我们知道单链表题型中有许多技巧, 其中有很多题都属于那种难者不会, 会者不难的题型, 满满的套路感, 下面整理了九道题目, 基本对应了七种技巧, 面试常考题型, 一起看看吧.

合并两个有序链表

题目链接: 合并两个有序链表
题目描述:
在这里插入图片描述

解题思路:

这道题就比较简单了, 属于单链表入门题型, 但是如果要让解法更简单, 最好定义一个虚拟头结点, 结果直接返回其next即可.

代码详解:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) 
    {
        // 定义一个虚拟头结点
        ListNode* newnode = new ListNode(-1), *p = newnode;

        ListNode* p1 = list1, *p2 = list2;

        // p1和p2均不为空时
        while(p1 && p2)
        {
            if(p1->val < p2->val)
            {
                p->next = p1;
                p1 = p1->next;
            }
            else
            {
                p->next = p2;
                p2 = p2->next;
            }
            p = p->next;
        }
		
		// p1不为空
        if(p1)
            p->next = p1;
		// p2不为空
        if(p2)
            p->next = p2;

        return newnode->next;    
    }
};

分隔链表

题目链接: 分隔链表
题目描述:
在这里插入图片描述
解题思路:

前面一题我们是将两条有序链表合并成一条有序链表, 这一题可以将原链表分割成为两条链表.
也就是说将原链表分割成为节点值小于x的短链表和节点值不小于x的短链表即可.

注意:
如果看代码不明白的话, 最好画图去理解.

代码详解:

class Solution {
public:
    ListNode* partition(ListNode* head, int x) 
    {
        // 定义两个虚拟头结点
        // 分成节点值小于x的短链表和节点值不小于x的短链表
        ListNode* newnode1 = new ListNode(-1), *p1 = newnode1;
        ListNode* newnode2 = new ListNode(-1), *p2 = newnode2;

        // 遍历比较原链表
        while(head != nullptr)
        {
            if(head->val < x)
            {
                p1->next = head;
                p1 = p1->next;
            }
            else
            {
                p2->next = head;
                p2 = p2->next;
            }

            head = head->next;
        }

        // 将节点值不小于x的短链表的尾置空
        p2->next = nullptr;
        p1->next = newnode2->next;

        return newnode1->next;
    }
};

合并K个有序链表

题目链接:合并K个有序链表
题目描述:
在这里插入图片描述
解题思路:

前面有讲到合并两个有序链表, 其实合并K个有序链表和它的解题思想很类似, 但是为了能够快速获取K个链表的最小节点, 我们需要定义一个最小堆, 将K个链表的头结点插入其中, 这样一来, 每次就可以获取K个节点的最小节点.

代码解析:

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        // 定义一个虚拟头结点, 将所有节点尾插其后
        ListNode* newnode = new ListNode(-1), *p = newnode;

        // 定义一个最小堆, 将K个链表的头结点插入其中
        // 用到了C++中新语法, 不熟悉的小伙伴可以去查阅哦
        priority_queue<ListNode*, vector<ListNode*>, function<bool(ListNode*, ListNode*)>> pq([] (ListNode* a, ListNode* b) {return a->val > b->val;});

        for(auto head : lists)
        {
            if(head != nullptr)
                pq.push(head);
        }

        while(!pq.empty())
        {
            // 获取堆顶节点, 即当前最小节点, 尾插到新链表后
            ListNode* pMin = pq.top();
            pq.pop();
            p->next = pMin;

            // 
            if(pMin->next != nullptr)
                pq.push(pMin->next);

            p = p->next;
        }

        return newnode->next;
    }
};

链表中倒数最后K个节点

题目链接: 链表中倒数最后K个节点
题目描述:
在这里插入图片描述
解题思路:

本题是想找到链表的最后K个节点, 前提是要找到倒数第K个节点, 如何在一次遍历的情况下找到呢?
首先定义两个指针p1和p2, 均指向链表的头结点. 一开始让p1先走k步(此时如果p1再走n-k步就走到链表的尾了), 这个时候p2从头和p1一起走, 直到p1指向nullptr为止, 此时p2在第n-k+1个节点上, 即倒数第k个位置, 大家可以画图去理解, 这里我就不画了哈哈哈哈.

代码详解:

class Solution {
public:

    ListNode* FindKthToTail(ListNode* pHead, int k) 
    {
        // write code here
        ListNode* p1 = pHead, *p2 = pHead;

        if(pHead == nullptr)
            return nullptr;

        // p1先走k步
        for(int i = 0; i < k; i++)
        {
            // 注意判断防止越界导致段错误
            if(p1)
                p1 = p1->next;
            else
                return nullptr;
        }

        // p2从头开始和p1一起走, 直至p1指向nullptr时, p2所在位置即为倒数第k个位置
        while(p1 != nullptr)
        {
            p1 = p1->next;
            p2 = p2->next;
        }

        return p2;
    }
};

变形题: 删除链表的倒数第N个节点

题目链接: 删除链表的倒数第N个节点
题目描述:
在这里插入图片描述
解题思路:

单链表中删除某一个节点, 前提是要先找到该节点的前一个节点, 同时为了防止头删第一个节点, 我们还需要定义一个虚拟头结点, 将head节点尾插其后.

代码解析:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
        // 定义一个虚拟头结点 - 注意单链表对虚拟头结点的运用
        ListNode* newnode = new ListNode(-1);
        newnode->next = head; // 防止头删第一个节点

        // 单链表中删除某一个节点, 前提需要找到它前一个节点
        ListNode* prev = findFromEnd(newnode, n + 1);
        
        prev->next = prev->next->next;

        return newnode->next;
    }

    // 单链表中找倒数第K个节点
    ListNode* findFromEnd(ListNode* head, int k)
    {
        // 定义两个指针p1和p2, p1先走k步(再走n-k步到尾), 然后p2从头开始h和p1一起走
        // 直到p1指向空, 此时p2在第n-k+1个节点的位置, 即倒数第k个节点
        ListNode* p1 = head, *p2 = head;

        if(head == nullptr)
            return nullptr;

        for(int i = 0; i < k; i++)
        {
            if(p1)
                p1 = p1->next;
            else
                return nullptr;
        }

        while(p1)
        {
            p1 = p1->next;
            p2 = p2->next;
        }

        return p2;
    }
};

链表的中点

题目链接: 链表的中点
题目描述:
在这里插入图片描述
解题思路:

利用快慢指针, 快指针一次走两步, 慢指针一次走一步, 当快指针指向末尾时, 慢指针所在的位置即是链表的中点.

代码详解:

class Solution {
public:
    ListNode* middleNode(ListNode* head) 
    {
        // 定义快慢指针
        ListNode* slow = head;
        ListNode* fast = head;

        // 快指针一次走两步, 慢指针一次走一步
        // 当快指针指向末尾时, 慢指针即是中点
        // 画图更好理解
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }

        return slow;
    }
};

判断链表是否有环

题目链接: 判断链表是否有环
题目描述:
在这里插入图片描述
解题思路:

利用快慢指针, 很简单不说了

代码详解:

class Solution {
public:
    ListNode* middleNode(ListNode* head) 
    {
        // 定义快慢指针
        ListNode* slow = head;
        ListNode* fast = head;

        // 快指针一次走两步, 慢指针一次走一步
        // 当快指针指向末尾时, 慢指针即是中点
        // 画图更好理解
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }

        return slow;
    }
};

环形链表II

题目链接: 环形链表II
题目描述:
在这里插入图片描述
解题思路:

下面是我两年前画的图…大家可以参考

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

class Solution {
public:
    ListNode *detectCycle(ListNode *head) 
    {
        // 定义快慢指针
        ListNode* slow = head;
        ListNode* fast = head;

        // 判断链表是否有环
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;

            if(slow == fast)
                break;
        }    

        // 循环非break退出, 说明没环
        if(fast == nullptr || fast->next == nullptr)
            return nullptr;

        // 否则有环, slow从头重新开始走
        // 二者再次相遇即为入口点
        slow = head;
        while(slow != fast)
        {
            slow = slow->next;
            fast = fast->next;
        }

        return slow;
    }
};

相交链表

题目链接: 相交链表
题目描述:
在这里插入图片描述
解题思路:

这题的解题思路就比较有意思了, 本题是求相交链表的交点, 难点在于什么, 就像上图一样, 链表A和链表B的长度很大可能不一样, 这样我们就做不到一次循环可以找到二者的交点, 那如何解决该难点呢?
我们可以定义两个指针p1和p2, 其中p1指向链表A, p2指向链表B. 一开始p1先遍历链表A然后再遍历链表B, 同理p2先遍历链表B再遍历链表A, 这样就可以保证二者遍历长度相同, 能够同时走到相交点.

代码分析:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        // 定义两个指针, p1指向链表A, p2指向链表B
        ListNode* p1 = headA;
        ListNode* p2 = headB;

        // 为了让p1和p2遍历链表的长度一样, 采用下面的做法
        while(p1 != p2)
        {
            // p1遍历完链表A, 开始遍历链表B
            if(p1 == nullptr)
                p1 = headB;
            else
                p1 = p1->next;

            // p2遍历完链表B, 开始遍历链表A
            if(p2 == nullptr)
                p2 = headA;
            else
                p2 = p2->next;
        }

        // 交点返回即可 - 画图去理解
        return p1;
    }
};

种一棵树最好的时间是十年前, 其次是现在.
一起加油, 我们很快就会再见.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/594514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Centos7安装Kubernetes 1.27.2

目录 一、准备工作 二、容器运行时 三、安装kubelet 、kubeadm、 kubectl 四、配置CNI 五、安装nginx 一、准备工作 1、更新yum源安装 vim、net-tools等工具&#xff08;每个节点都执行&#xff09; yum update -yyum install vim -yyum install net-tools -y 2、配置每…

简单移位器结构介绍

移位器 一位可控移位器 其实是一个复杂的多路开关电路&#xff0c;根据不同控制信号&#xff0c;将输入左移或右移或不变。多位的移位可以简单串联这样的单元实现&#xff0c;但移位位数多时&#xff0c;该方法过于复杂&#xff0c;不实用并且速度很慢。 桶形移位器 由晶体管…

C.12 军事领域关系抽取:UIE Slim最新升级版含数据标注、serving部署、模型蒸馏、可视化高亮展示等,助力工业应用场景快速落地

NLP专栏简介:数据增强、智能标注、意图识别算法|多分类算法、文本信息抽取、多模态信息抽取、可解释性分析、性能调优、模型压缩算法等 专栏详细介绍:NLP专栏简介:数据增强、智能标注、意图识别算法|多分类算法、文本信息抽取、多模态信息抽取、可解释性分析、性能调优、模型…

Linux内核漏洞提权

目录 Linux提权辅助工具 内核漏洞本地用户提权 - linux-exploit-suggester测试 内核漏洞Web用户提权 - 利用脏牛dcow 内核漏洞本地用户提权 - 利用DirtyPipe&#xff08;脏管&#xff09; 配置安全SUID提权 Linux提权辅助工具 这些工具都是C\C编写的&#xff0c;需要在目…

vmware虚拟机设置双网卡

文章目录 1. 配置虚拟机NAT模式2. 配置虚拟机桥接网络2.1 通过USB网卡2.1.1 配置虚拟机桥接网卡ip:2.1.2 配置windows主机桥接网卡ip:2.1.3 配置板子ip: 2.2 通过路由器2.2.1 配置板子ip: NAT 网卡&#xff1a;Ubuntu 通过它上网&#xff0c;只要 Windows 能上网&#xff0c;Ub…

北邮22信通:实验六 由运放器构成的音频放大电路设计、仿真、测试报告

北邮22信通一枚~ 持续更新模电实验讲解 关注作者&#xff0c;解锁更多邮苑模电实验报告~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22信通——电子电路_青山如墨雨如画的博客-CSDN博客 目录 实验目的&#xff1a; 设计要求&#xff1a; *补充&#xff1…

AI最新资讯

AI最新资讯 1.画图2.修图3.3D建模4.openai调用5.自媒体工具6.自动化网页制作 自从gpt火了之后&#xff0c;AIGC就更新很快&#xff0c;许多好用的插件都太多了&#xff0c;所以记录一下&#xff0c;方便之后用到。 1.画图 1.midjourney和playgroundAI我之前博客有写过。 2.最近…

Web基本漏洞--文件上传漏洞

目录 一、文件上传漏洞介绍 1.文件上传漏洞原理 2.文件上传漏洞识别 3. 攻击方式 4.文件上传漏洞的危害 5.文件上传漏洞的防御措施 6.文件上传漏洞的绕过 一、文件上传漏洞介绍 1.文件上传漏洞原理 文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺…

ActiveMQ消息中间件简介

一、ActiveMQ简介 ActiveMQ是Apache出品&#xff0c;最流行的&#xff0c;能力强劲的开源消息总线。ActiveMQ是一个完全支持JMS1.1和J2EE1.4规范的JMS Provide实现。尽管JMS规范出台已经是很久的事情了&#xff0c;但是JMS在当今的J2EE应用中仍然扮演这特殊的地位。 二、Active…

3Dmax云渲染如何使用?一文带你了解云渲染

3ds Max是Autodesk公司推出的一款专业计算机图形和三维动画软件&#xff0c;被广泛应用于建筑、室内设计、电影、游戏、广告、工业设计等领域。它提供丰富的模型建模、纹理编辑、灯光设置、渲染等功能&#xff0c;可以制作出高质量的三维模型、动画和静态渲染。它具有强大的扩展…

【1++的Linux】之Linux权限

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;什么是Linux权限&#xff1f;二&#xff0c;Linux权限管理2.1 文件访问者的分类2.2 文件类型和访问权限2.3 修改文件权限2.4 掩码的作用及其设置 三&#xf…

前沿技术|人工智能的崛起和发展历程

前言&#xff1a; 人工智能的作用是使计算机能够模仿人类智能和学习能力&#xff0c;从而实现自动化、智能化和优化决策的目标。 文章目录 人工智能背景介绍发展状态未来展望 总结 人工智能 背景 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;的产生…

29Maven高级

一、分模块设计与开发 1、介绍 2、tlias-pojo模块 然后把pojo复制过去。pom中加入lombok依赖 引入tlias-pojo依赖 3、tlias-utils模块 tlias-utils的pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.…

高频面试八股文原理篇(一)hashmap原理相关

目录 引言 面试题&#xff1a;hashmap原理 原理 JDK7时HashMap的数据结构 jdk8中hashMap数据结构 hashMap怎么设置初始值的大小 jdk7和jdk8中HashMap的区别 为什么放在hashMap集合key部分的元素需要重写equals方法&#xff1f; concurrenthashmap为什么线程安全 高频面…

Linux文件的扩展属性

文件属性 Linux文件属性分为常规属性与扩展属性&#xff0c;其中扩展属性有两种&#xff1a;attr与xattr. 一般常规的文件属性由stat API 读取&#xff0c;一般是三种权限&#xff0c;ower, group&#xff0c;时间等。 扩展属性attr 用户态API ioctl(fd, FS_IOC32_SETFLAGS …

从零开始学习JavaScript:轻松掌握编程语言的核心技能②

从零开始学习JavaScript&#xff1a;轻松掌握编程语言的核心技能② 一&#xff0c;JavaScript 函数1.1 JavaScript 函数语法1.2 局部 JavaScript 变量1.3 全局 JavaScript 变量 二&#xff0c;JavaScript 作用域2.1JavaScript 局部作用域 三&#xff0c;JavaScript 事件3.1HTML…

Linux——磁盘文件的理解 + inode详解 + 软硬链接 +动静态库

索引 磁盘文件的理解inode软硬链接动静态库理解动态链接与地址无关生成动静态库使用静态库使用动态库动态链接再次理解&#xff01; 磁盘文件的理解 之前讲述的都是内存级的文件&#xff0c;但是系统中最多的还是磁盘级的文件&#xff0c;大量的文件自然也需要被管理&#xff…

【算法】08 耦合 Lorenz 63 模式的参数估计实验

摘要 本实验通过建立基于耦合 Lorenz 63 模式的孪生实验框架&#xff0c;使用集合调整卡尔曼滤波器EAKF实现参数估计&#xff0c;测试参数估计开始的不同阶段、观测误差、同化频率、协方差膨胀等方法细节对于参数估计结果的影响。 耦合 Lorenz 63 模式 Lorenz 63 模式是Lore…

Postman实战:轻松搞定接口自动化测试

随着移动互联网的发展&#xff0c;接口自动化测试已经成为软件测试领域中不可或缺的一部分。而作为最流行的API开发工具之一&#xff0c;Postman凭借其简单易用、功能强大的特点赢得了越来越多开发者和测试人员的青睐。 想要掌握Postman的接口自动化测试技能&#xff0c;只需要…