关于链表相关的OJ题

news2024/12/23 23:28:31

✨✨✨专栏:数据结构     

          🧑‍🎓个人主页:SWsunlight

一、 OJ题 返回倒数第K个节点:

 1、遍历链表一遍:用2个指针,pheadptail先让ptail先走k步,然后让2个指针一起走,快的走到NULL即可

就是数学问题:第n-k个就是倒数第k个

2、也可以遍历一遍算出节点个数n,然后倒数第k个就是节点n-k,在遍历一次即可

代码:

int kthToLast(struct ListNode* head, int k){
    struct ListNode*phead=head;
    struct ListNode*ptail=head;
    //先让快的走k步
    while(k--)
    {
        ptail=ptail->next;
    }
    while(ptail)
    {
         ptail=ptail->next;
         phead=phead->next;
    }
    return phead->val;

}

falst 和 slow

二、OJ题 合并2个有序链表:

 注意:题目说的链表是有序的,那么我们可以建立一个哨兵位 然后将各个节点放到哨兵位的后面 进行有序排序即可:

//改名,便于使用
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;
    ListNode*tail;
    ListNode*l1 = list1;
    ListNode*l2 = list2;
    head = tail = (ListNode*)malloc(sizeof(ListNode));
    //只要有一个为NULL就出来;
    while(l1&&l2)
    {
        //小的先放入后面
        if(l1->val<l2->val)
        {
            tail->next = l1;
            tail=tail->next;
            l1 = l1->next;
        }
        else
        {
            tail->next = l2;
            tail = tail->next;
            l2 = l2->next;
        }
    }
    //出循环2种情况:l1和l2都为NULL
    //              l1或者l2 一个为NULL]
    //不要用循环了,因为其中一个为NULL,tail后面直接就是加上了另一个链表的剩下的节点
    if(l1)
    {
        tail->next = l1;
        tail = tail->next;
    }
    if(l2)
    {
        tail->next = l2;
        tail = tail->next;
    }
    ListNode*ret = head->next;
    //申请的空间还给操作系统
    free(head);
    head = NULL;
    return ret;
}

 三、相交链表:

关于这个题,可以先考虑是否相交遍历(同时记录2个指针的长度)对最后一个节点地址进行判断是否相等(相等就是说明这个环相交),若是不相交直接结束程序了;

若是相交,我们就得继续考虑:发现了么,若是相交,看头节点的位置,2个链表会有一个距离差,这个时候我们可以长的链表先走完距离差,然后2个链表一起遍历,同速度一定相遇

注意:我们用地址那判断,不用节点里的数据是因为无法保证后前面数据有相同的,地址更保险

typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    ListNode*l1=headA;
    ListNode*l2=headB;
    //记录节点长度
    int k = 0;
    int n = 0;
    //l1、l2的位置不能到NULL,不然这不就一定相等了(链表的最后一个节点指向的下一个地址为NULL)
    while(l1&&l1->next)
    {
        l1 = l1->next;
        k++;
    }
    while(l2&&l2->next)
    {
        l2 = l2->next;
        n++;
    }
    //若是最后一个节点不相等 说明不为环
    if(l1!=l2)
    {
        return NULL;
    }
    //为环继续进行;
    ListNode* phead1 = headA;
    ListNode* phead2 = headB;
    //假设法:1放长的,2放小的链表
    if(k<n)
    {
        phead1 = headB;
        phead2 = headA;
    }
    //求2个的链表的长度差
    //abc为绝对值函数,返回绝对值
    int key = abs(n-k);
    while(key--)
    {
        phead1=phead1->next;
    }
    //同时遍历,相同时结束
    while(phead1!=phead2)
    {
        phead1 = phead1->next;
        phead2 = phead2->next;
    }
    return phead1;

}

用到了假设法,还有 绝对值函数:abs()函数

四、链表的中间节点:

快慢指针即可,快的指针到到尾节点时,慢指针刚好走了一半;不要考虑节点个数为奇数还是偶数,因为题目说了若是2个中间节点返回第2个,如下:当快指针结束会有2种情况,刚好跟节点个数有关 记住节点个数:n      中间节点的公式: n /  2   表示从第一个节点的下一个节点开始数,第几个节点就是中间节点

typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast&&fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

五、反转链表:

看到题目可能会想到创建一个新链表,将原链表的节点以头插的形式插入到新链表中,但是我今天想用的是不创建新的链表,使用迭代的方式进行实现反转

将l1当做尾节点依次类推  

用到了3个指针  l3指向NULL,l2用来保存下个节点的地址,l1用来修改所指向节点的next的地址   

移动步骤如下:1、修改l1的next指向l3

                         2、将l3移动到l1的位置

                         3、将l1移动到l2的位置

                         4、将l2后移一位

结束条件 我们将l2作为条件,当l2指向空时结束,如下:但是l1的next是还没有变的,使用if语句判断 进行修改 即可,最后返回 l1 刚好是头节点的地址

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
    //判NULL,若是为空则直接返回
    if(head==NULL)
    {
        return head;
    } 
    //l2保存下一个节点地址,l1修改其所在节点的next 
    ListNode*l1 = head;
    ListNode*l2 = head->next;
    //存储l1->next的节点,开始时是NULL,因为头节点变成尾节点的next指向的内容为NULL
    ListNode*l3 = NULL;
    while(l2)
    {
        l1->next = l3;
        l3 = l1;
        l1 = l2;
        l2 = l2->next;
        //当l2NULL;l1在尾节点,直接修改尾节点的next内容,
        if(l2==NULL)
        {
            l1->next = l3;
        }
    }
    return l1;
}

注意 :要判空,不然l2赋值时就非法操作了(对空指针进行解引用)

 六、链表的回文结构:

快慢指针法:

看到回文结构,我们可以用到中间节点和反转链表2个方法结合,CV一下;我们将链表的中间节点后面的节点进行反转,然后进行判断

这里取的是第3个节点进行反转,但是因为我么的第2个节点next指针指向的是第三个,我们没有对它修改,所以拆开以后就是如下的样子:

无论奇数还是偶数都没有影响

typedef struct ListNode ListNode;

class PalindromeList {
  public:
   
    struct ListNode* middleNode(struct ListNode* head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while (fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
    struct ListNode* reverseList(struct ListNode* head) {
        //判NULL,若是为空则直接返回
        if (head == NULL) {
            return head;
        }
        //l2保存下一个节点地址,l1修改其所在节点的next
        ListNode* l1 = head;
        ListNode* l2 = head->next;
        //存储l1->next的节点,开始时是NULL,因为头节点变成尾节点的next指向的内容为NULL
        ListNode* l3 = NULL;
        while (l2) {
            l1->next = l3;
            l3 = l1;
            l1 = l2;
            l2 = l2->next;
            //当l2NULL;l1在尾节点,直接修改尾节点的next内容,
            if (l2 == NULL) {
                l1->next = l3;
            }
        }
        return l1;
    }

    bool chkPalindrome(ListNode* A) {
        //pet接受后半部分的反转链表
        ListNode* pet = middleNode(A);
        ListNode*A1 =A;
        ListNode*A2 =reverseList(pet);
        //进行比较
        while(A2)
        {
            if(A2->val!=A1->val)
            {
                return false;
            }
            A2=A2->next;
            A1=A1->next;
        }
        return true;


    }
};

我在牛客网写的这个题目,因为c++是兼容c的牛客题没有c语言这个环境的选项,就直接在c++里面写了

七、移除链表元素: 

3个指针 :

将节点取出来创建成一个“新”的链表(一个记录头一个记录尾)再来一个遍历链表,遍历到NULL时结束循环;以上面题目提供的例子为例的话,当我将所有节点取出重新组合后,为下图第一个图形,发现会有一个6没去掉,因为节点指向下一个节点的next没有修改,所以最后循环结束应该要加一个判断条件 尾指针是否为NULL,不是NULL,就给它next赋值NULL;

typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
    //先判空
    if(head==NULL)
    {
        return head;
    }
    //记录头结点
    ListNode* phead= NULL;
    //记录尾节点
    ListNode*ptail = NULL;
    //用来遍历:
    ListNode*pcur=head;
    while(pcur)
    {
       if(pcur->val!=val)
       {
        //是否为空链表
        if(phead==NULL)
        {
            phead= ptail=pcur;
        }
        else
        {
            ptail->next=pcur;
            ptail = ptail->next;
        }
       }
       pcur = pcur->next;
    }
    //出循环以后,若是ptail不是空,将下一个地址给空
    if(ptail)
    {
        ptail->next=NULL;
    }
 
    return phead;
}

 八、随机链表的复制:

错位插入,复制每个节点,然后和原链表以如下方式连接起来,形成一个新的链表

如下:要向堆区申请空间,要创建一个节点

给random分配

typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {
    //创立一个新的节点
    Node*phead = head;
    //phead为NULL时结束
    while(phead)
    {
        //申请节点,创建新的节点,需要申请空间
        Node*node = (Node*)malloc(sizeof(Node));
        node->val = phead->val;
        //我的节点next为head的下一个节点地址
        node->next=phead->next;
        //初始情况:
        node->random = NULL;
        //phead的next指向node
        phead->next=node;
        phead = node->next;
    }
    //对上面的部分random进行分配
    Node* cal = head;
    while(cal)
    { 
        //cal的下一个节点就是要分配的节点
        Node* pov = cal->next;
        if(cal->random==NULL)
        {
            pov->random=NULL;
        }
        else
        {
            pov->random = cal->random->next;
        }
        cal = pov->next;
    }
    //拆开  //裁掉
    cal = head;
    //尾
    Node* povhead=NULL;
    //头不动
    Node* nude = NULL;
    while(cal)
    {
    //判空
     if(povhead==NULL)
     {
        povhead = nude = cal->next;
     }
     else{
        povhead->next = cal->next;
        povhead = povhead->next;

     }

     //恢复原链表
     cal->next = povhead->next;
     cal = cal->next;
        
    }

    return nude;

}

 

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

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

相关文章

计算机发展史故事【13】

微电脑先锋 与第一台电子计算机埃历阿克的命运相似&#xff0c;1974 年面世的“牛郎星”能否作为世界上第一台微电脑被载入史册&#xff0c;人们似乎也存在着分歧。 拥有微处理器发明权的英特尔公司&#xff0c;难道自己不会组装微电脑&#xff0c;非得罗伯茨来越俎代庖吗&…

从零开始:C++ String类的模拟实现

文章目录 引言1.类的基本结构2.构造函数和析构函数3.基本成员函数总结 引言 在C编程中&#xff0c;字符串操作是非常常见且重要的任务。标准库中的std::string类提供了丰富且强大的功能&#xff0c;使得字符串处理变得相对简单。然而&#xff0c;对于学习C的开发者来说&#x…

【考研数学】强化阶段,张宇《1000题》正确率达到多少算合格?

首次正确率在60%以上就算是合格&#xff01; 张宇老师的1000题真挺难的&#xff0c;所以如果第一次做正确率不高&#xff0c;不要太焦虑&#xff0c;1000题不管是难度&#xff0c;综合度还是计算量&#xff0c;都比其他的题集高一截。 大家真实的做题情况下&#xff0c;如果正…

前端工程化 - 快速通关 - ES6

目录 ES6 1.1 let 1.2 const 1.3解构 1.4链判断 1.5参数默认值 1.6箭头函数 1.7模板字符串 1.8Promise 1.9Async 函数 1.10模块化 ES6 ●ECMAScript&#xff08;ES&#xff09; 是规范、 JavaScript 是 ES 的实现 ●ES6 的第一个版本 在 2015 年 6 月发布&#xff0c…

【C语言习题】12.扫雷游戏

文章目录 1.扫雷游戏分析和设计1.1 扫雷游戏的功能说明1.2游戏界面&#xff1a;1.3游戏的分析和设计1.2.1 数据结构的分析1.2.2 ⽂件结构设计 2.扫雷游戏的代码实现3.代码讲解 1.扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 使用控制台实现经典的扫雷游戏游戏可以通过菜单实现…

机器学习案例:加州房产价格(四)

参考链接&#xff1a;https://hands1ml.apachecn.org/2/#_12 数据探索和可视化、发现规律 通过之前的工作&#xff0c;你只是快速查看了数据&#xff0c;对要处理的数据有了整体了解&#xff0c;现在的目标是更深的探索数据。 首先&#xff0c;保证你将测试集放在了一旁&…

特征模态分解(FMD):一种小众而又新颖的分解方法

​ 声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 今天为大家介绍一个小众而又新颖的信号分…

HyperLogLog的使用做UV统计

使用Jedis连接Redis并操作HyperLogLog import redis.clients.jedis.Jedis;public class RedisHyperLogLogExample {public static void main(String[] args) {// 连接到本地的Redis服务Jedis jedis new Jedis("localhost", 6379);String hyperLogLogKey "hll…

智慧安防系统:构建更安全的社区环境

随着科技的不断进步&#xff0c;人们的生活质量得到了显著提高。然而&#xff0c;与此同时&#xff0c;社会治安问题也日益凸显。为了维护社会的和谐稳定&#xff0c;提高人们的生活安全感&#xff0c;智慧安防系统应运而生。本文将为您详细介绍智慧安防系统的项目背景、需求分…

默认成员函数:析构、深浅拷贝

析构函数 析构函数&#xff1a;与构造函数功能相反&#xff0c;析构函数不是完成对对象本身的销毁&#xff0c;局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数&#xff0c;完成对象中资源的清理工作。 特性 析构函数名时在类名前面加上字符~ class D…

Python 全栈体系【四阶】(四十三)

第五章 深度学习 九、图像分割 3. 常用模型 3.4 DeepLab 系列 3.4.1 DeepLab v1(2015) 3.4.1.1 概述 图像分割和图像分类不一样&#xff0c;要对图像每个像素进行精确分类。在使用CNN对图像进行卷积、池化过程中&#xff0c;会导致特征图尺寸大幅度下降、分辨率降低&…

旧手机-基于Termux配置服务器(不用拿去换钢盆了)

Hi&#xff0c;大家好&#xff0c;我是抢老婆酸奶的小肥仔。 大家用手机这么多年了&#xff0c;手上或多或少都有一两个被替换下来的旧手机&#xff0c;也不用拿去换啥钢盆了&#xff0c;使用Termux可以将旧手机改造成一个服务器。 不多说&#xff0c;直接开干。 1、安装app…

Java应用程序的本地内存跟踪分析

本文将讨论本机内存跟踪 (NMT)&#xff0c;我们可以使用它来隔离在 VM 级别增长的任何异常内存。 1.什么是本机内存&#xff1f; 本机内存是指计算机系统上运行的应用程序或程序可直接访问的内存空间。它是程序在执行期间存储和操作数据的内存区域。本机内存不同于托管内存&a…

单调栈练习

最大矩形面积 如果栈为空&#xff0c;或者新的元素是大于栈顶元素的&#xff0c;那么新来的元素不会破坏栈的单调性&#xff0c;那么就把这个柱子入栈。 特别注意&#xff1a;这里的s.empty()和s.top().height < a不能调换顺序&#xff0c;包括后面的判断也要先判断栈是否为…

分析 vs2019 cpp20 规范的 STL 库模板 function ,源码注释并探讨几个问题

&#xff08;1 探讨一&#xff09;第一个尝试弄清的问题是父类模板与子类模板的模板参数的对应关系&#xff0c;如下图&#xff1a; 我们要弄清的问题是创建 function 对象时&#xff0c;传递的模板参数 _Fty , 传递到其父类 _Func_class 中时 &#xff0c;父类的模板参数 _Ret…

QT状态机4-使用并行状态来避免组合爆炸

#include "MainWindow.h" #include "ui_MainWindow.h"MainWindow::MainWindow(QWidget *parent):

基于EKF扩展卡尔曼滤波的一阶环形倒立摆控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于EKF扩展卡尔曼滤波的一阶环形倒立摆控制系统simulink建模与仿真。基于扩展卡尔曼滤波&#xff08;Extended Kalman Filter, EKF&#xff09;的一阶环形倒立摆控制系统&…

分布式搜索-elaticsearch基础 安装es

这里是在虚拟机的Linux系统里安装es的教程: 1.创建网络 在Finashell终端输入指令 docker network create es-net 2.将es.tar安装包放入tmp的目录下 输入指令加载镜像&#xff1a;docker load -i es.tar 3.再运行docker 命令 docker run -d \--name es \-e "ES_JAVA_O…

NVM安装及VUE创建项目的N种方式

VUE 参考官网&#xff1a;https://cli.vuejs.org/zh/guide/ NVM安装 我们开发过程中常常遇到nodejs版本不适配的问题&#xff0c;需要切换到不同版本的nodejs&#xff0c;nodejs卸载安装麻烦&#xff0c;这就需要用到nvm了。 nvm 全名 node.js version management 顾名思义是…

基于SVPWM的飞轮控制系统的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于SVPWM的飞轮控制系统的simulink建模与仿真。SVPWM的核心思想是将逆变器输出的三相电压矢量在两相静止坐标系&#xff08;αβ坐标系&#xff09;中表示&#xff0c;通过控…