【链表面试题考察】

news2024/12/23 10:47:53

以下题目均为IO型。

1.给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

题目示例如上:

解题思路:

双指针问题,给定指针prev和cur,从头结点开始往后遍历,分两种情况讨论:

  1. 假如头节点为要删除的节点

  1. 假如中间节点为要删除的节点

prev记录要删除节点的前一个节点,cur记录要删除的节点

假如是第一种情况,cur->val == val时,

            if(cur == head)//1.头删
            {
                head = cur->next;
                free(cur);
                cur = head;
            }

假如是第二种情况,

prev->next = cur->next;
free(cur);
cur = prev->next;

完整代码如下:

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


//画图分析才会写
typedef struct ListNode ListNode;
 
struct ListNode* removeElements(struct ListNode* head, int val)
{
    ListNode*cur = head;
    ListNode*prev = head;
    while(cur)
    {
        if(cur->val==val)
        {
            if(cur == head)//1.头删
            {
                head = cur->next;
                free(cur);
                cur = head;
            }
            else//2.中间删除
            {
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}

2.给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

反转链表较为简单,下面介绍两种方法:

  1. 三指针遍历法

给定三个指针prev为NULL,cur = head,next = cur->next,这样往后遍历即可。

typedef struct ListNode ListNode;
法1,三指针往后迭代,prev,cur,next往后,画图才会写
struct ListNode* reverseList(struct ListNode* head)
{
    ListNode*prev = NULL;
    ListNode*cur = head;
    while(head)
    {
        cur = cur->next;
        head ->next = prev;
        prev = head;
        head = cur;
    }
    return prev;
}
  1. 头插法

typedef struct ListNode ListNode;
法2,头插

struct ListNode* reverseList(struct ListNode* head)
{
    ListNode*newhead = NULL;
    ListNode*cur = head;
    while(cur)
    {
        ListNode*next = cur->next; 
        //头插
        cur->next = newhead;
        newhead = cur;

        //迭代往后
        cur = next;
    }
    return newhead;
}

3.给定一个头结点为 head 的非空单链表,返回链表的中间点。如果有两个中间结点,则返回第二个中间结点。

只需要使用两个指针,fast和slow指针即可,快指针一次走两步,慢指针一次走一步,这样快指针走完时,慢指针一定走到中间节点。

//最优解:快慢指针,慢指针一次走一步,快指针一次走两步
struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode*slow = head,*fast = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

4.输入一个链表,输出该链表中倒数第k个结点。

解题思路:仍然使用快慢指针:但是这样的快慢指针不是像上一道题快指针走两步,慢指针走一步。

这里的快慢指针是快指针先走,慢指针后走。

fast先走k步,然后fast和slow同时走。

当fast == NULL时,slow即为倒数第k个

原因:快指针先走的k步,消除了倒数的概念。

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

5.将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

解题思路:

  1. 使用带哨兵位的头节点。2。不使用带哨兵位的头节点

  1. 使用带哨兵位的头节点时,好处在于不需要将最小的节点作为头节点,劣势在于由于哨兵位的头节点是malloc出来的,使用完之后需要释放,在释放之间还需要保存哨兵位的头节点的next。

哨兵位的头节点的好处是,不需要再把第一个小的值给head
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
带哨兵位的头解法:

    if(list1==NULL)
        return list2;
    if(list2 == NULL)
        return list1;
    //带哨兵位的头节点
    ListNode*head = NULL;
    head = (ListNode*)malloc(sizeof(ListNode));
    ListNode*tail = head;

    while(list1 && list2)
    {   
        if(list1->val < list2->val)
        {
            tail->next = list1;
            tail = list1;
            list1 = list1->next;
        }
        else
        {
            tail->next = list2;    
            tail = list2;
            list2 = list2->next;
        }
    }
    if(list1)
    {
        tail->next = list1;
    }
    if(list2)
    {
        tail->next = list2;
    }
    //注意这里,不能用prevhead = head,然后return prevhead->next
    //因为prev = head,head已经free了,相当于prev非法访问了。
    //所以只能prev = head->next,return prev;
    ListNode*prevhead = head->next;
    free(head);
    return prevhead;
}

6.现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

举个简单的例子:

上面的链表是原链表,定值x是4,也就是小于4的节点需要在前面,大于4的节点在后面,并且不能改变原来链表的节点的顺序。下面的链表即为题目要求的链表。

注意:

由于原链表中的6的next仍然指向2,会造成出现环形链表的情况,会造成死循环,所以需要将6的next 置为NULL。

思路:将小于定值x和大于定值x的链表分开,分别连接后,最后在将小的链表和大的链表连接起来。

class Partition {
  public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode*LessHead ,*LessTail,*GreaterHead,*GreaterTail;
        LessHead=LessTail=GreaterHead=GreaterTail= NULL;
        LessTail = LessHead= (struct ListNode*)malloc(sizeof(struct ListNode));
        GreaterTail= GreaterHead = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* cur = pHead;
        while (cur) 
        {
            if (cur->val < x)
            {
                LessTail->next = cur;
                LessTail = cur;
            } 
            else 
            {
                GreaterTail->next = cur;
                GreaterTail = cur;
            }
            cur = cur ->next;
        }
        //到这里说明cur走完链表了,此时需要将两个链表连接起来
        LessTail->next = GreaterHead->next;
        //注意:一定要将GreaterTail->next = NULL
        //假如链表为2->3->5->7->6->2,GreaterTail->next还指向2
        //造成链表成环形了,所以要置空
        GreaterTail->next = NULL;
        ListNode* newnode = LessHead->next;
        free(LessHead);
        free(GreaterHead);
        return newnode;
    }
};

7.

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

思路:所谓的回文结构,其实就是判断链表是否对称。

  1. 先找出链表的中间节点,然后以该中间节点为界,将链表分割。

  1. 前面的链表称为list1,后面的链表称为list2。

  1. 将list2逆置,再将list1和list2的节点逐个判断,如果list1或者list2的节点都相等,那就是回文结构。

//链表回文的思路:
// 1、先找出链表的中间节点,(偶数个找第二个中间节点)
// 2.中间节点作为新的链表头进行逆置。
// 将新逆置后的链表和原链表的val相比,(因为原链表的某些节点的next存储着新链表的某些节点的地址)
ListNode* MidListNode(ListNode* A)
{
    ListNode*slow =A,*fast = A;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

ListNode* reverseListNode(ListNode* mid)
{
    ListNode*prev = NULL;
    ListNode*cur = mid;
    while(cur)
    {
        ListNode*pnext = cur->next;
        cur->next = prev;
        prev = cur;
        //迭代往后
        cur = pnext;
    }
    return prev;
}

class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        // write code heretypedef struct ListNode ListNode;
        // write code here
        ListNode*mid = MidListNode(A);
        ListNode*rhead = reverseListNode(mid);
        ListNode*curA = A;
        ListNode*curR = rhead;
        while(curA && curR)
        {
            if(curA->val!=curR->val)
            {
                return false;
            }
            else
            {
                curA = curA->next;
                curR = curR->next;
            }
        }
        return true;
    }
};

8.给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

思路:给定一个fast指针和slow指针,先分别遍历两个链表,记录长度,让fast指针指向长链表,fast指针先走 长链表长度-链表长度,随后slow指针从短链表的头开始,和fast链表一起往后走,每个指针一次走一步,如果相等,则相等的点为相交点。

原因:快指针先走的长度差消除了长短链表的长度差。

 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    ListNode*curA = headA;
    ListNode*curB = headB;
    
    int i = 0;
    int j = 0;
    //先记录两个链表的长度,随后让长链表先走k步,再一起走,就消除了长度差,时间复杂度为O(N),空间复杂度为O(1);

    //还可以再遍历完两个链表的同时,记录两个链表的尾结点,如果尾结点不相同,那肯定不相交,直接返回NULL
    ListNode*TailA = NULL;
    ListNode*TailB = NULL;
    while(curA)
    {
        ++i;
        TailA = curA;
        curA = curA->next;
    }
    while(curB)
    {
        ++j;
        TailB = curB;
        curB = curB->next;
    }
    if(TailA !=TailB)
    {
        return NULL;
    }
    if(i>j)
    {
        int k = i-j;
        curA = headA;
        curB = headB;
        while(k--)
        {
            curA = curA->next;//长链表先走k步
        }
        while(curB && curA)
        {
            if(curA == curB)
            {
                return curA;
            }
            else
            {
                curA = curA->next;
                curB = curB->next;
            }
        }
    }
    else
    {
        int k = j-i;
        curA = headA;
        curB = headB;
        while(k--)
        {
            curB = curB->next;//长链表先走k步
        }
        while(curB && curA)
        {
            if(curA == curB)
            {
                return curA;
            }
            else
            {
                curA = curA->next;
                curB = curB->next;
            }
        }
    }
    return NULL;
}

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

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

相关文章

Unity SKFramework Documentation

文章目录Audio 音频背景音乐音效音频库Audio ListenerActions 事件Action 事件类型Action Chain 事件链Sequence 序列事件链Concurrent 并发事件链Timeline 时间轴事件链FSM 有限状态机State 状态State Machine 状态机State Builder 状态构建器ObjectPool 对象池IPoolable 接口…

2023 年 1 月的5篇深度学习论文推荐

本文整理了 2023 年 1 月5 篇著名的 AI 论文&#xff0c;涵盖了计算机视觉、自然语言处理等方面的新研究。 InstructPix2Pix: Learning to Follow Image Editing Instructions https://arxiv.org/abs/2211.09800v2 伯克利分校的研究人员开发了一种使用人工指令编辑图像的新方…

小程序项目学习--**第三章:WXSS-WXML-WXS语法**事件处理-组件化开发

第三章&#xff1a;WXSS-WXML-WXS语法事件处理-组件化开发 01_(掌握)WXML语法-基本规则和mustache语法 Page({data: {message: "Hello World",firstname: "kobe",lastname: "bryant",date: new Date().toLocaleDateString(),}, }) <!-- 1.Mu…

【HBase高级】5. HBase数据结构(上)跳表、二叉搜索树、红黑树、B、B+树

4. HBase事务 HBase 支持特定场景下的 ACID&#xff0c;即当对同一行进行 Put 操作时保证完全的 ACID。可以简单理解为针对一行的操作&#xff0c;是有事务性保障的。HBase也没有混合读写事务。也就是说&#xff0c;我们无法将读操作、写操作放入到一个事务中。 5. HBase数据…

CMMI高效落地 4大关键点要注意

CMM对企业降本增效、增强竞争力方面&#xff0c;优势明显。那么如何顺利进行CMMI认证&#xff1f;我们在CMMI认证时&#xff0c;需要注意哪些方面&#xff1f; 1、公司高层的支持 一个公司过程改进 工作的顺利施行&#xff0c;首先需要公司高层的支持。公司的商业目标、公司高层…

45_API接口漏洞

API接口漏洞 一、概念 api > application interface 应用接口 向特定的接口发送一个请求包 返回一个类似于json格式的字符串 二、REST型web service 可以从网上去搜索下api接口去理解,下面有个我找到的网址,给出api接口的分类 https://blog.csdn.net/t79036912/article…

【顺序表和链表的对比】

前言&#xff1a; 我们已经学习过了顺序表和链表的一些知识&#xff0c;在实际运用中我们不能笼统的说哪种存储结构更好&#xff0c;由于它们各有优缺点&#xff0c;选择哪种存储结构&#xff0c;则应该根据具体问题作出具体的分析&#xff0c;通常从空间性能和时间性能上作比较…

Day14【元宇宙的实践构想03】—— 元宇宙的资产观(NFT、数字资产、虚拟地产、与现实世界资产关系)

&#x1f483;&#x1f3fc; 本人简介&#xff1a;男 &#x1f476;&#x1f3fc; 年龄&#xff1a;18 ✍今日内容&#xff1a;《元宇宙的实践构想》03——元宇宙的资产观 ❗❗❗从1.31日开始&#xff0c;阿亮每天会查阅一些元宇宙方面的小知识&#xff0c;和大家一起分享。一是…

cobaltstrike的shellcode免杀

基础概念 shellcode是一段用于利用软件漏洞而执行的代码&#xff0c;也可以认为是一段填充数据&#xff0c;shellcode为16进制的机器码&#xff0c;因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 可在暂存器eip溢出后&#xff0c;塞入一段可让CPU执行的s…

vue入门到精通(七)

6、依赖注入 祖先组件向后代组件传值 6.1 provide() 提供一个值&#xff0c;可以被后代组件注入。 provide() 接受两个参数&#xff1a;第一个参数是要注入的 key&#xff0c;可以是一个字符串或者一个 symbol&#xff0c;第二个参数是要注入的值。 与注册生命周期钩子的 AP…

百趣代谢组学文献分享埃博拉病毒发病机制及组合生物标志物的发现

百趣代谢组学文献分享&#xff0c;今天我们分享的文献就是通过多组学技术研究埃博拉病毒发病机制及组合生物标志物的发现。该文献的研究思路也可以给我们开展新型冠状病毒肺炎相关研究提供借鉴。 代谢组学文献分享&#xff0c;2013-2016年西非埃博拉病毒病&#xff08;EVD&…

(面经三,技术面)——时间:2022-11-11 地点:线上

面试经历&#xff08;三&#xff09;——时间&#xff1a;2022-11-11 地点&#xff1a;线上 1.什么是抽象类 有抽象方法的类&#xff0c;用来表征对问题领域进行分析、设计中得出的抽象概念。 2.抽象类和接口的区别 继承关系&#xff1a;类只能单继承。接口可以实现多个接口 构…

智慧物业管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

【大数据】第二章:搭建Hadoop集群(送尚硅谷大数据笔记)

尚硅谷Hadoop3.x官方文档大全免费下载 搭建集群没什么好讲的&#xff0c;跟着视频和笔记出不了什么问题。 唯一遇到的问题就是安装好VmWare后打不开&#xff0c;发现是老师给的VmWare版本不适配本机的WIN11。 解决办法就是下载最新版本的VmWare。新版已经修复了与WIN11的兼容性…

计算机网络基础(三)

前言&#xff1a; 在计算机网络基础(二)中&#xff0c;我们着重学习了应用层&#xff0c;传输层的知识。在 本文&#xff0c;就要介绍网络层&#xff0c;数据链路层&#xff0c;这两块内容细节也很多。这是计算机网络基础篇的最后一文&#xff0c;系统的学习后&#xff0c;就可…

基于php、Thinkphp5的共享电动车管理系统

摘 要当前共享单车在社会上广泛使用,但单车骑行的短距离仍旧不能完全满足广大用户的需求。共享电动车管理系统可以为用户提供账户信息、押金信息、充值信息、租车信息等功能,拥有较好的用户体验.能实时动态显示车辆位置提供更加快捷方便的租车方式,解决了常见共享电动车管理较为…

英雄互娱|提升 300% !一次性能优化实战记录

案例背景 英雄互娱是国内知名游戏研发商和发行商&#xff0c;经常遇到热门线上游戏&#xff0c;在某瞬间出现大量登录请求&#xff0c;需要临时扩容资源的场景。为了让服务更好的应对突增并发请求压力&#xff0c;客户尝试通过把应用服务容器化部署&#xff0c;能通过 HPA&…

Android 屏幕刷新机制 VSync+Choreographer

1.显示系统基础知识 一个典型的显示系统一般包括CPU、GPU、Display三部分,其中CPU负责计算帧数据,并把计算好的数据交给GPU,GPU会对图形数据进行渲染,渲染好后放到图像缓冲区buffet里存起来,然后Display(屏幕或显示器)负责把buffer里的数据呈现到屏幕上。如下图: 这里…

npm发布封装的公共组件

一.新建vue项目项目目录结构如下:二.修改项目文件夹1.创建一个packages文件夹(用于存放编写的组件)2.把src修改为examples3.新建一个vue.config.js文件,并修改由于修改了src文件夹,启动vue项目后,找不到入口(main.js)会报错,所以需要重新指定启动入口module.exports {// 将 ex…

大数据实时多维OLAP分析数据库Apache Druid入门分享-下

文章目录架构核心架构外部依赖核心内容roll-up预聚合列式存储Datasource和Segments位图索引数据摄取查询集群部署部署规划前置条件MySQL配置HDFS配置Zookeeper配置启动集群导入HDFS示例架构 核心架构 Druid servers建议将它们组织为三种服务器类型:Master主服务器、Query查询服…